fix corner case in `unused` (#5190)
[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                         if (value.has_side_effects(compressor) && sym.match_symbol(function(node) {
7215                             return node instanceof AST_PropAccess;
7216                         })) break;
7217                         value = make_node(AST_Sub, node, {
7218                             expression: value,
7219                             property: make_node(AST_Number, node, { value: 0 }),
7220                         });
7221                         return sym;
7222                     }
7223                     fill_holes(node, elements);
7224                     node.elements = elements;
7225                     return node;
7226                 }
7227                 if (node instanceof AST_DestructuredObject) {
7228                     var save_drop = drop;
7229                     var save_value = value;
7230                     if (value instanceof AST_SymbolRef) {
7231                         drop = false;
7232                         value = value.fixed_value();
7233                     }
7234                     var prop_keys, prop_map;
7235                     if (value instanceof AST_Object) {
7236                         prop_keys = [];
7237                         prop_map = Object.create(null);
7238                         value.properties.forEach(function(prop, index) {
7239                             if (prop instanceof AST_Spread) return prop_map = false;
7240                             var key = prop.key;
7241                             if (key instanceof AST_Node) key = key.evaluate(compressor, true);
7242                             if (key instanceof AST_Node) {
7243                                 prop_map = false;
7244                             } else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
7245                                 prop_map[key] = prop;
7246                             }
7247                             prop_keys[index] = key;
7248                         });
7249                     }
7250                     if (node.rest) {
7251                         value = false;
7252                         node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7253                     }
7254                     var can_drop = Object.create(null);
7255                     var drop_keys = drop && Object.create(null);
7256                     var properties = [];
7257                     node.properties.map(function(prop) {
7258                         var key = prop.key;
7259                         if (key instanceof AST_Node) {
7260                             prop.key = key = key.transform(tt);
7261                             key = key.evaluate(compressor, true);
7262                         }
7263                         if (key instanceof AST_Node) {
7264                             drop_keys = false;
7265                         } else {
7266                             can_drop[key] = !(key in can_drop);
7267                         }
7268                         return key;
7269                     }).forEach(function(key, index) {
7270                         var prop = node.properties[index], trimmed;
7271                         if (key instanceof AST_Node) {
7272                             drop = false;
7273                             value = false;
7274                             trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
7275                         } else {
7276                             drop = drop_keys && can_drop[key];
7277                             var mapped = prop_map && prop_map[key];
7278                             if (mapped) {
7279                                 value = mapped.value;
7280                                 if (value instanceof AST_Accessor) value = false;
7281                             } else {
7282                                 value = false;
7283                             }
7284                             trimmed = prop.value.transform(trimmer);
7285                             if (!trimmed) {
7286                                 if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
7287                                 if (drop_keys && !(key in drop_keys)) {
7288                                     if (mapped) {
7289                                         drop_keys[key] = mapped;
7290                                         if (value === null) {
7291                                             prop_map[key] = retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
7292                                                 key: mapped.key,
7293                                                 value: make_node(AST_Number, mapped, { value: 0 }),
7294                                             });
7295                                         }
7296                                     } else {
7297                                         drop_keys[key] = true;
7298                                     }
7299                                 }
7300                             } else if (drop_keys) {
7301                                 drop_keys[key] = false;
7302                             }
7303                             if (value) mapped.value = value;
7304                         }
7305                         if (trimmed) {
7306                             prop.value = trimmed;
7307                             properties.push(prop);
7308                         }
7309                     });
7310                     value = save_value;
7311                     drop = save_drop;
7312                     if (drop_keys && prop_keys) value.properties = List(value.properties, function(prop, index) {
7313                         if (prop instanceof AST_Spread) return prop;
7314                         var key = prop_keys[index];
7315                         if (key instanceof AST_Node) return prop;
7316                         if (key in drop_keys) {
7317                             var mapped = drop_keys[key];
7318                             if (!mapped) return prop;
7319                             if (mapped === prop) return prop_map[key] || List.skip;
7320                         } else if (node.rest) {
7321                             return prop;
7322                         }
7323                         var trimmed = prop.value.drop_side_effect_free(compressor);
7324                         if (trimmed) {
7325                             prop.value = trimmed;
7326                             return prop;
7327                         }
7328                         return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, {
7329                             key: prop.key,
7330                             value: make_node(AST_Number, prop, { value: 0 }),
7331                         }) : List.skip;
7332                     });
7333                     if (value && !node.rest) switch (properties.length) {
7334                       case 0:
7335                         if (value.may_throw_on_access(compressor, true)) break;
7336                         if (drop) value = value.drop_side_effect_free(compressor);
7337                         return null;
7338                       case 1:
7339                         if (!drop) break;
7340                         var prop = properties[0];
7341                         if (prop.key instanceof AST_Node) break;
7342                         if (prop.value.has_side_effects(compressor)) break;
7343                         if (value.has_side_effects(compressor) && prop.value.match_symbol(function(node) {
7344                             return node instanceof AST_PropAccess;
7345                         })) break;
7346                         value = make_node(AST_Sub, node, {
7347                             expression: value,
7348                             property: make_node_from_constant(prop.key, prop),
7349                         });
7350                         return prop.value;
7351                     }
7352                     node.properties = properties;
7353                     return node;
7354                 }
7355                 if (node instanceof AST_Hole) {
7356                     node = null;
7357                 } else {
7358                     node = process(node);
7359                 }
7360                 if (!node && drop && value) value = value.drop_side_effect_free(compressor);
7361                 return node;
7362             });
7363             return {
7364                 name: node.transform(trimmer),
7365                 value: value,
7366             };
7367
7368             function retain_key(prop) {
7369                 return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor);
7370             }
7371
7372             function retain_lhs(node) {
7373                 if (node instanceof AST_DefaultValue) return retain_lhs(node.name);
7374                 if (node instanceof AST_Destructured) {
7375                     if (value === null) {
7376                         value = make_node(AST_Number, node, { value: 0 });
7377                     } else if (value && (value.tail_node().write_only === true
7378                         || value.may_throw_on_access(compressor, true))) {
7379                         value = make_node(AST_Array, node, {
7380                             elements: value instanceof AST_Sequence ? value.expressions : [ value ],
7381                         });
7382                     }
7383                     return make_node(AST_DestructuredObject, node, { properties: [] });
7384                 }
7385                 node.__unused = null;
7386                 return node;
7387             }
7388         }
7389     });
7390
7391     AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
7392         if (compressor.has_directive("use asm")) return;
7393         var hoist_funs = compressor.option("hoist_funs");
7394         var hoist_vars = compressor.option("hoist_vars");
7395         var self = this;
7396         if (hoist_vars) {
7397             // let's count var_decl first, we seem to waste a lot of
7398             // space if we hoist `var` when there's only one.
7399             var var_decl = 0;
7400             self.walk(new TreeWalker(function(node) {
7401                 if (var_decl > 1) return true;
7402                 if (node instanceof AST_ExportDeclaration) return true;
7403                 if (node instanceof AST_Scope && node !== self) return true;
7404                 if (node instanceof AST_Var) {
7405                     var_decl++;
7406                     return true;
7407                 }
7408             }));
7409             if (var_decl <= 1) hoist_vars = false;
7410         }
7411         if (!hoist_funs && !hoist_vars) return;
7412         var consts = Object.create(null);
7413         var dirs = [];
7414         var hoisted = [];
7415         var vars = new Dictionary(), vars_found = 0;
7416         var tt = new TreeTransformer(function(node, descend, in_list) {
7417             if (node === self) return;
7418             if (node instanceof AST_Directive) {
7419                 dirs.push(node);
7420                 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7421             }
7422             if (node instanceof AST_LambdaDefinition) {
7423                 if (!hoist_funs) return node;
7424                 var p = tt.parent();
7425                 if (p instanceof AST_ExportDeclaration) return node;
7426                 if (p instanceof AST_ExportDefault) return node;
7427                 if (p !== self && compressor.has_directive("use strict")) return node;
7428                 hoisted.push(node);
7429                 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7430             }
7431             if (node instanceof AST_Var) {
7432                 if (!hoist_vars) return node;
7433                 var p = tt.parent();
7434                 if (p instanceof AST_ExportDeclaration) return node;
7435                 if (!all(node.definitions, function(defn) {
7436                     var sym = defn.name;
7437                     return sym instanceof AST_SymbolVar
7438                         && !consts[sym.name]
7439                         && self.find_variable(sym.name) === sym.definition();
7440                 })) return node;
7441                 node.definitions.forEach(function(def) {
7442                     vars.set(def.name.name, def);
7443                     ++vars_found;
7444                 });
7445                 var seq = node.to_assignments();
7446                 if (p instanceof AST_ForEnumeration && p.init === node) {
7447                     if (seq) return seq;
7448                     var def = node.definitions[0].name;
7449                     return make_node(AST_SymbolRef, def, def);
7450                 }
7451                 if (p instanceof AST_For && p.init === node) return seq;
7452                 if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7453                 return make_node(AST_SimpleStatement, node, { body: seq });
7454             }
7455             if (node instanceof AST_Scope) return node;
7456             if (node instanceof AST_SymbolConst) {
7457                 consts[node.name] = true;
7458                 return node;
7459             }
7460         });
7461         self.transform(tt);
7462         if (vars_found > 0) {
7463             // collect only vars which don't show up in self's arguments list
7464             var defs = [];
7465             if (self instanceof AST_Lambda) self.each_argname(function(argname) {
7466                 vars.del(argname.name);
7467             });
7468             vars.each(function(def, name) {
7469                 def = def.clone();
7470                 def.value = null;
7471                 defs.push(def);
7472                 vars.set(name, def);
7473             });
7474             if (defs.length > 0) {
7475                 // try to merge in assignments
7476                 insert_vars(self.body);
7477                 defs = make_node(AST_Var, self, { definitions: defs });
7478                 hoisted.push(defs);
7479             }
7480         }
7481         self.body = dirs.concat(hoisted, self.body);
7482
7483         function insert_vars(body) {
7484             while (body.length) {
7485                 var stat = body[0];
7486                 if (stat instanceof AST_SimpleStatement) {
7487                     var expr = stat.body, sym, assign;
7488                     if (expr instanceof AST_Assign
7489                         && expr.operator == "="
7490                         && (sym = expr.left) instanceof AST_Symbol
7491                         && vars.has(sym.name)) {
7492                         var def = vars.get(sym.name);
7493                         if (def.value) break;
7494                         var value = expr.right;
7495                         if (value instanceof AST_Sequence) value = value.clone();
7496                         def.value = value;
7497                         remove(defs, def);
7498                         defs.push(def);
7499                         body.shift();
7500                         continue;
7501                     }
7502                     if (expr instanceof AST_Sequence
7503                         && (assign = expr.expressions[0]) instanceof AST_Assign
7504                         && assign.operator == "="
7505                         && (sym = assign.left) instanceof AST_Symbol
7506                         && vars.has(sym.name)) {
7507                         var def = vars.get(sym.name);
7508                         if (def.value) break;
7509                         def.value = assign.right;
7510                         remove(defs, def);
7511                         defs.push(def);
7512                         stat.body = make_sequence(expr, expr.expressions.slice(1));
7513                         continue;
7514                     }
7515                 }
7516                 if (stat instanceof AST_EmptyStatement) {
7517                     body.shift();
7518                     continue;
7519                 }
7520                 if (stat instanceof AST_BlockStatement && !insert_vars(stat.body)) {
7521                     body.shift();
7522                     continue;
7523                 }
7524                 break;
7525             }
7526             return body.length;
7527         }
7528     });
7529
7530     function scan_local_returns(fn, transform) {
7531         fn.walk(new TreeWalker(function(node) {
7532             if (node instanceof AST_Return) {
7533                 transform(node);
7534                 return true;
7535             }
7536             if (node instanceof AST_Scope && node !== fn) return true;
7537         }));
7538     }
7539
7540     function map_bool_returns(fn) {
7541         var map = Object.create(null);
7542         scan_local_returns(fn, function(node) {
7543             var value = node.value;
7544             if (value) value = value.tail_node();
7545             if (value instanceof AST_SymbolRef) {
7546                 var id = value.definition().id;
7547                 map[id] = (map[id] || 0) + 1;
7548             }
7549         });
7550         return map;
7551     }
7552
7553     function all_bool(def, bool_returns, compressor) {
7554         return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length - def.replaced
7555             && !compressor.exposed(def);
7556     }
7557
7558     function process_boolean_returns(fn, compressor) {
7559         scan_local_returns(fn, function(node) {
7560             node.in_bool = true;
7561             var value = node.value;
7562             if (value) {
7563                 var ev = fuzzy_eval(compressor, value);
7564                 if (!ev) {
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: 0 }),
7569                     ]) : null;
7570                 } else if (!(ev instanceof AST_Node)) {
7571                     value = value.drop_side_effect_free(compressor);
7572                     node.value = value ? make_sequence(node.value, [
7573                         value,
7574                         make_node(AST_Number, node.value, { value: 1 }),
7575                     ]) : make_node(AST_Number, node.value, { value: 1 });
7576                 }
7577             }
7578         });
7579     }
7580
7581     AST_Scope.DEFMETHOD("process_boolean_returns", noop);
7582     AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
7583         if (!compressor.option("booleans")) return;
7584         var bool_returns = map_bool_returns(this);
7585         if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
7586         if (compressor.parent() instanceof AST_ExportDefault) return;
7587         process_boolean_returns(this, compressor);
7588     });
7589     AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
7590         if (!compressor.option("booleans")) return;
7591         var bool_returns = map_bool_returns(this);
7592         if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
7593         var parent = compressor.parent();
7594         if (parent instanceof AST_Assign) {
7595             if (parent.operator != "=") return;
7596             var sym = parent.left;
7597             if (!(sym instanceof AST_SymbolRef)) return;
7598             if (!all_bool(sym.definition(), bool_returns, compressor)) return;
7599         } else if (parent instanceof AST_Call && parent.expression !== this) {
7600             var exp = parent.expression;
7601             if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
7602             if (!(exp instanceof AST_Lambda)) return;
7603             if (exp.uses_arguments || exp.pinned()) return;
7604             var sym = exp.argnames[parent.args.indexOf(this)];
7605             if (sym instanceof AST_DefaultValue) sym = sym.name;
7606             if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
7607         } else if (parent.TYPE == "Call") {
7608             compressor.pop();
7609             var in_bool = compressor.in_boolean_context();
7610             compressor.push(this);
7611             if (!in_bool) return;
7612         } else return;
7613         process_boolean_returns(this, compressor);
7614     });
7615
7616     AST_BlockScope.DEFMETHOD("var_names", function() {
7617         var var_names = this._var_names;
7618         if (!var_names) {
7619             this._var_names = var_names = Object.create(null);
7620             this.enclosed.forEach(function(def) {
7621                 var_names[def.name] = true;
7622             });
7623             this.variables.each(function(def, name) {
7624                 var_names[name] = true;
7625             });
7626         }
7627         return var_names;
7628     });
7629
7630     AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
7631         var scopes = [ this ];
7632         if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
7633             var s = ref.scope;
7634             if (member(s, scopes)) return;
7635             do {
7636                 push_uniq(scopes, s);
7637                 s = s.parent_scope;
7638             } while (s && s !== this);
7639         });
7640         prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
7641         var name = prefix;
7642         for (var i = 0; !all(scopes, function(scope) {
7643             return !scope.var_names()[name];
7644         }); i++) name = prefix + "$" + i;
7645         var sym = make_node(type, orig, {
7646             name: name,
7647             scope: this,
7648         });
7649         var def = this.def_variable(sym);
7650         scopes.forEach(function(scope) {
7651             scope.enclosed.push(def);
7652             scope.var_names()[name] = true;
7653         });
7654         return sym;
7655     });
7656
7657     AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
7658         if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
7659         var self = this;
7660         var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
7661         var defs_by_id = Object.create(null);
7662         self.transform(new TreeTransformer(function(node, descend) {
7663             if (node instanceof AST_Assign) {
7664                 if (node.operator != "=") return;
7665                 if (!node.write_only) return;
7666                 if (!can_hoist(node.left, node.right, 1)) return;
7667                 descend(node, this);
7668                 var defs = new Dictionary();
7669                 var assignments = [];
7670                 var decls = [];
7671                 node.right.properties.forEach(function(prop) {
7672                     var decl = make_sym(AST_SymbolVar, node.left, prop.key);
7673                     decls.push(make_node(AST_VarDef, node, {
7674                         name: decl,
7675                         value: null,
7676                     }));
7677                     var sym = make_node(AST_SymbolRef, node, {
7678                         name: decl.name,
7679                         scope: self,
7680                         thedef: decl.definition(),
7681                     });
7682                     sym.reference();
7683                     assignments.push(make_node(AST_Assign, node, {
7684                         operator: "=",
7685                         left: sym,
7686                         right: prop.value,
7687                     }));
7688                 });
7689                 defs.value = node.right;
7690                 defs_by_id[node.left.definition().id] = defs;
7691                 self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
7692                     definitions: decls,
7693                 }));
7694                 return make_sequence(node, assignments);
7695             }
7696             if (node instanceof AST_Scope) return node === self ? undefined : node;
7697             if (node instanceof AST_VarDef) {
7698                 if (!can_hoist(node.name, node.value, 0)) return;
7699                 descend(node, this);
7700                 var defs = new Dictionary();
7701                 var var_defs = [];
7702                 var decl = node.clone();
7703                 decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null;
7704                 var_defs.push(decl);
7705                 node.value.properties.forEach(function(prop) {
7706                     var_defs.push(make_node(AST_VarDef, node, {
7707                         name: make_sym(node.name.CTOR, node.name, prop.key),
7708                         value: prop.value,
7709                     }));
7710                 });
7711                 defs.value = node.value;
7712                 defs_by_id[node.name.definition().id] = defs;
7713                 return List.splice(var_defs);
7714             }
7715
7716             function make_sym(type, sym, key) {
7717                 var new_var = self.make_var(type, sym, sym.name + "_" + key);
7718                 defs.set(key, new_var.definition());
7719                 return new_var;
7720             }
7721         }));
7722         self.transform(new TreeTransformer(function(node, descend) {
7723             if (node instanceof AST_PropAccess) {
7724                 if (!(node.expression instanceof AST_SymbolRef)) return;
7725                 var defs = defs_by_id[node.expression.definition().id];
7726                 if (!defs) return;
7727                 if (node.expression.fixed_value() !== defs.value) return;
7728                 var def = defs.get(node.get_property());
7729                 var sym = make_node(AST_SymbolRef, node, {
7730                     name: def.name,
7731                     scope: node.expression.scope,
7732                     thedef: def,
7733                 });
7734                 sym.reference();
7735                 return sym;
7736             }
7737             if (node instanceof AST_SymbolRef) {
7738                 var defs = defs_by_id[node.definition().id];
7739                 if (!defs) return;
7740                 if (node.fixed_value() !== defs.value) return;
7741                 return make_node(AST_Object, node, { properties: [] });
7742             }
7743         }));
7744
7745         function can_hoist(sym, right, count) {
7746             if (!(sym instanceof AST_Symbol)) return;
7747             var def = sym.definition();
7748             if (def.assignments != count) return;
7749             if (def.references.length - def.replaced == count) return;
7750             if (def.single_use) return;
7751             if (top_retain(def)) return;
7752             if (sym.fixed_value() !== right) return;
7753             var fixed = sym.fixed || def.fixed;
7754             if (fixed.direct_access) return;
7755             if (fixed.escaped && fixed.escaped.depth == 1) return;
7756             return right instanceof AST_Object
7757                 && right.properties.length > 0
7758                 && all(right.properties, can_hoist_property)
7759                 && can_drop_symbol(sym, compressor);
7760         }
7761     });
7762
7763     function fn_name_unused(fn, compressor) {
7764         if (!fn.name || !compressor.option("ie")) return true;
7765         var def = fn.name.definition();
7766         if (compressor.exposed(def)) return false;
7767         return all(def.references, function(sym) {
7768             return !(sym instanceof AST_SymbolRef);
7769         });
7770     }
7771
7772     // drop_side_effect_free()
7773     // remove side-effect-free parts which only affects return value
7774     (function(def) {
7775         // Drop side-effect-free elements from an array of expressions.
7776         // Returns an array of expressions with side-effects or null
7777         // if all elements were dropped. Note: original array may be
7778         // returned if nothing changed.
7779         function trim(nodes, compressor, first_in_statement, spread) {
7780             var len = nodes.length;
7781             var ret = [], changed = false;
7782             for (var i = 0; i < len; i++) {
7783                 var node = nodes[i];
7784                 var trimmed;
7785                 if (spread && node instanceof AST_Spread) {
7786                     trimmed = spread(node, compressor, first_in_statement);
7787                 } else {
7788                     trimmed = node.drop_side_effect_free(compressor, first_in_statement);
7789                 }
7790                 if (trimmed !== node) changed = true;
7791                 if (trimmed) {
7792                     ret.push(trimmed);
7793                     first_in_statement = false;
7794                 }
7795             }
7796             return ret.length ? changed ? ret : nodes : null;
7797         }
7798         function array_spread(node, compressor, first_in_statement) {
7799             var exp = node.expression;
7800             if (!exp.is_string(compressor)) return node;
7801             return exp.drop_side_effect_free(compressor, first_in_statement);
7802         }
7803         function convert_spread(node) {
7804             return node instanceof AST_Spread ? make_node(AST_Array, node, {
7805                 elements: [ node ]
7806             }) : node;
7807         }
7808         def(AST_Node, return_this);
7809         def(AST_Accessor, return_null);
7810         def(AST_Array, function(compressor, first_in_statement) {
7811             var values = trim(this.elements, compressor, first_in_statement, array_spread);
7812             if (!values) return null;
7813             if (values === this.elements && all(values, function(node) {
7814                 return node instanceof AST_Spread;
7815             })) return this;
7816             return make_sequence(this, values.map(convert_spread));
7817         });
7818         def(AST_Assign, function(compressor) {
7819             var left = this.left;
7820             if (left instanceof AST_PropAccess) {
7821                 var expr = left.expression;
7822                 if (expr.may_throw_on_access(compressor, true)) return this;
7823                 if (compressor.has_directive("use strict") && expr.is_constant()) return this;
7824             }
7825             if (left.has_side_effects(compressor)) return this;
7826             if (lazy_op[this.operator.slice(0, -1)]) return this;
7827             this.write_only = true;
7828             if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this;
7829             return this.right.drop_side_effect_free(compressor);
7830         });
7831         def(AST_Await, function(compressor) {
7832             if (!compressor.option("awaits")) return this;
7833             var exp = this.expression;
7834             if (!is_primitive(compressor, exp)) return this;
7835             var node = this.clone();
7836             node.expression = exp.drop_side_effect_free(compressor) || make_node(AST_Number, this, { value: 0 });
7837             return node;
7838         });
7839         def(AST_Binary, function(compressor, first_in_statement) {
7840             var left = this.left;
7841             var right = this.right;
7842             var op = this.operator;
7843             if (op == "in" && !is_object(right)) {
7844                 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7845                 if (lhs === left) return this;
7846                 var node = this.clone();
7847                 node.left = lhs || make_node(AST_Number, left, { value: 0 });
7848                 return node;
7849             }
7850             var rhs = right.drop_side_effect_free(compressor, first_in_statement);
7851             if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement);
7852             if (lazy_op[op] && rhs.has_side_effects(compressor)) {
7853                 var node = this;
7854                 if (rhs !== right) {
7855                     node = node.clone();
7856                     node.right = rhs.drop_side_effect_free(compressor);
7857                 }
7858                 if (op == "??") return node;
7859                 var negated = make_node(AST_Binary, this, {
7860                     operator: op == "&&" ? "||" : "&&",
7861                     left: left.negate(compressor, first_in_statement),
7862                     right: node.right,
7863                 });
7864                 return first_in_statement ? best_of_statement(node, negated) : best_of_expression(node, negated);
7865             }
7866             var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7867             if (!lhs) return rhs;
7868             rhs = rhs.drop_side_effect_free(compressor);
7869             if (!rhs) return lhs;
7870             return make_sequence(this, [ lhs, rhs ]);
7871         });
7872         function drop_returns(compressor, exp) {
7873             var arrow = is_arrow(exp);
7874             var async = is_async(exp);
7875             var drop_body = false;
7876             if (arrow && compressor.option("arrows")) {
7877                 if (!exp.value) {
7878                     drop_body = true;
7879                 } else if (!async || is_primitive(compressor, exp.value)) {
7880                     exp.value = exp.value.drop_side_effect_free(compressor);
7881                 }
7882             } else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
7883                 if (exp.name) {
7884                     var def = exp.name.definition();
7885                     drop_body = def.references.length == def.replaced;
7886                 } else {
7887                     drop_body = true;
7888                 }
7889             }
7890             if (drop_body) {
7891                 exp.process_expression(false, function(node) {
7892                     var value = node.value;
7893                     if (value) {
7894                         if (async && !is_primitive(compressor, value)) return node;
7895                         value = value.drop_side_effect_free(compressor, true);
7896                     }
7897                     if (!value) return make_node(AST_EmptyStatement, node);
7898                     return make_node(AST_SimpleStatement, node, { body: value });
7899                 });
7900                 scan_local_returns(exp, function(node) {
7901                     var value = node.value;
7902                     if (value) {
7903                         if (async && !is_primitive(compressor, value)) return;
7904                         node.value = value.drop_side_effect_free(compressor);
7905                     }
7906                 });
7907             }
7908             if (async && compressor.option("awaits")) {
7909                 if (drop_body) exp.process_expression("awaits", function(node) {
7910                     var body = node.body;
7911                     if (body instanceof AST_Await) {
7912                         if (is_primitive(compressor, body.expression)) {
7913                             body = body.expression.drop_side_effect_free(compressor, true);
7914                             if (!body) return make_node(AST_EmptyStatement, node);
7915                             node.body = body;
7916                         }
7917                     } else if (body instanceof AST_Sequence) {
7918                         var exprs = body.expressions;
7919                         for (var i = exprs.length; --i >= 0;) {
7920                             var tail = exprs[i];
7921                             if (!(tail instanceof AST_Await)) break;
7922                             if (!is_primitive(compressor, tail.expression)) break;
7923                             if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
7924                         }
7925                         switch (i) {
7926                           case -1:
7927                             return make_node(AST_EmptyStatement, node);
7928                           case 0:
7929                             node.body = exprs[0];
7930                             break;
7931                           default:
7932                             exprs.length = i + 1;
7933                             break;
7934                         }
7935                     }
7936                     return node;
7937                 });
7938                 var abort = !drop_body && exp.name || arrow && exp.value && !is_primitive(compressor, exp.value);
7939                 var tw = new TreeWalker(function(node) {
7940                     if (abort) return true;
7941                     if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
7942                     if (node instanceof AST_Await) return abort = true;
7943                     if (node instanceof AST_ForAwaitOf) return abort = true;
7944                     if (node instanceof AST_Return) {
7945                         if (node.value && !is_primitive(compressor, node.value)) return abort = true;
7946                         return;
7947                     }
7948                     if (node instanceof AST_Scope && node !== exp) return true;
7949                 });
7950                 exp.walk(tw);
7951                 if (!abort) {
7952                     var ctor;
7953                     switch (exp.CTOR) {
7954                       case AST_AsyncArrow:
7955                         ctor = AST_Arrow;
7956                         break;
7957                       case AST_AsyncFunction:
7958                         ctor = AST_Function;
7959                         break;
7960                       case AST_AsyncGeneratorFunction:
7961                         ctor = AST_GeneratorFunction;
7962                         break;
7963                     }
7964                     return make_node(ctor, exp, exp);
7965                 }
7966             }
7967             return drop_body && exp.clone();
7968         }
7969         def(AST_Call, function(compressor, first_in_statement) {
7970             var self = this;
7971             if (self.is_expr_pure(compressor)) {
7972                 if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
7973                 var args = trim(self.args, compressor, first_in_statement, array_spread);
7974                 return args && make_sequence(self, args.map(convert_spread));
7975             }
7976             var exp = self.expression;
7977             if (self.is_call_pure(compressor)) {
7978                 var exprs = self.args.slice();
7979                 exprs.unshift(exp.expression);
7980                 exprs = trim(exprs, compressor, first_in_statement, array_spread);
7981                 return exprs && make_sequence(self, exprs.map(convert_spread));
7982             }
7983             if (compressor.option("yields") && is_generator(exp)) {
7984                 var call = self.clone();
7985                 call.expression = make_node(AST_Function, exp, exp);
7986                 call.expression.body = [];
7987                 var opt = call.transform(compressor);
7988                 if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
7989             }
7990             var dropped = drop_returns(compressor, exp);
7991             if (dropped) {
7992                 // always shallow clone to ensure stripping of negated IIFEs
7993                 self = self.clone();
7994                 self.expression = dropped;
7995                 // avoid extraneous traversal
7996                 if (exp._squeezed) self.expression._squeezed = true;
7997             }
7998             if (self instanceof AST_New) {
7999                 var fn = exp;
8000                 if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
8001                 if (fn instanceof AST_Lambda) {
8002                     if (assign_this_only(fn, compressor)) {
8003                         var exprs = self.args.slice();
8004                         exprs.unshift(exp);
8005                         exprs = trim(exprs, compressor, first_in_statement, array_spread);
8006                         return exprs && make_sequence(self, exprs.map(convert_spread));
8007                     }
8008                     if (!fn.contains_this()) self = make_node(AST_Call, self, self);
8009                 }
8010             }
8011             self.call_only = true;
8012             return self;
8013         });
8014         function assign_this_only(fn, compressor) {
8015             fn.new = true;
8016             var result = all(fn.body, function(stat) {
8017                 return !stat.has_side_effects(compressor);
8018             }) && all(fn.argnames, function(argname) {
8019                 return !argname.match_symbol(return_false);
8020             }) && !(fn.rest && fn.rest.match_symbol(return_false));
8021             delete fn.new;
8022             return result;
8023         }
8024         function drop_class(self, compressor, first_in_statement) {
8025             var exprs = [], values = [];
8026             var props = self.properties;
8027             for (var i = 0; i < props.length; i++) {
8028                 var prop = props[i];
8029                 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8030                 if (prop.static && prop.value
8031                     && prop instanceof AST_ClassField
8032                     && prop.value.has_side_effects(compressor)) {
8033                     if (prop.value.contains_this()) return self;
8034                     values.push(prop.value);
8035                 }
8036             }
8037             var base = self.extends;
8038             if (base) {
8039                 if (base instanceof AST_SymbolRef) base = base.fixed_value();
8040                 base = !safe_for_extends(base);
8041                 if (!base) exprs.unshift(self.extends);
8042             }
8043             exprs = trim(exprs, compressor, first_in_statement);
8044             if (exprs) first_in_statement = false;
8045             values = trim(values, compressor, first_in_statement);
8046             if (!exprs) {
8047                 if (!base && !values) return null;
8048                 exprs = [];
8049             }
8050             if (base) {
8051                 var node = to_class_expr(self, true);
8052                 node.properties = [];
8053                 if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, {
8054                     key: make_sequence(self, exprs),
8055                     value: make_node(AST_Function, self, {
8056                         argnames: [],
8057                         body: [],
8058                     }).init_vars(node),
8059                 }));
8060                 exprs = [ node ];
8061             }
8062             if (values) exprs.push(make_node(AST_Call, self, {
8063                 expression: make_node(AST_Arrow, self, {
8064                     argnames: [],
8065                     body: [],
8066                     value: make_sequence(self, values),
8067                 }).init_vars(self.parent_scope),
8068                 args: [],
8069             }));
8070             return make_sequence(self, exprs);
8071         }
8072         def(AST_ClassExpression, function(compressor, first_in_statement) {
8073             var self = this;
8074             var name = self.name;
8075             if (name && name.fixed_value() !== self && name.definition().references.length > 0) return self;
8076             return drop_class(self, compressor, first_in_statement);
8077         });
8078         def(AST_Conditional, function(compressor) {
8079             var consequent = this.consequent.drop_side_effect_free(compressor);
8080             var alternative = this.alternative.drop_side_effect_free(compressor);
8081             if (consequent === this.consequent && alternative === this.alternative) return this;
8082             var exprs;
8083             if (compressor.option("ie")) {
8084                 exprs = [];
8085                 if (consequent instanceof AST_Function) {
8086                     exprs.push(consequent);
8087                     consequent = null;
8088                 }
8089                 if (alternative instanceof AST_Function) {
8090                     exprs.push(alternative);
8091                     alternative = null;
8092                 }
8093             }
8094             var node;
8095             if (!consequent) {
8096                 node = alternative ? make_node(AST_Binary, this, {
8097                     operator: "||",
8098                     left: this.condition,
8099                     right: alternative
8100                 }) : this.condition.drop_side_effect_free(compressor);
8101             } else if (!alternative) {
8102                 node = make_node(AST_Binary, this, {
8103                     operator: "&&",
8104                     left: this.condition,
8105                     right: consequent
8106                 });
8107             } else {
8108                 node = this.clone();
8109                 node.consequent = consequent;
8110                 node.alternative = alternative;
8111             }
8112             if (!compressor.option("ie")) return node;
8113             if (node) exprs.push(node);
8114             return exprs.length == 0 ? null : make_sequence(this, exprs);
8115         });
8116         def(AST_Constant, return_null);
8117         def(AST_DefClass, function(compressor, first_in_statement) {
8118             return drop_class(this, compressor, first_in_statement);
8119         });
8120         def(AST_Dot, function(compressor, first_in_statement) {
8121             var expr = this.expression;
8122             if (!this.optional && expr.may_throw_on_access(compressor)) return this;
8123             return expr.drop_side_effect_free(compressor, first_in_statement);
8124         });
8125         def(AST_Function, function(compressor) {
8126             return fn_name_unused(this, compressor) ? null : this;
8127         });
8128         def(AST_LambdaExpression, return_null);
8129         def(AST_Object, function(compressor, first_in_statement) {
8130             var exprs = [];
8131             this.properties.forEach(function(prop) {
8132                 if (prop instanceof AST_Spread) {
8133                     exprs.push(prop);
8134                 } else {
8135                     if (prop.key instanceof AST_Node) exprs.push(prop.key);
8136                     exprs.push(prop.value);
8137                 }
8138             });
8139             var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
8140                 var exp = node.expression;
8141                 return spread_side_effects(exp) ? node : exp.drop_side_effect_free(compressor, first_in_statement);
8142             });
8143             if (!values) return null;
8144             if (values === exprs && !all(values, function(node) {
8145                 return !(node instanceof AST_Spread);
8146             })) return this;
8147             return make_sequence(this, values.map(function(node) {
8148                 return node instanceof AST_Spread ? make_node(AST_Object, node, {
8149                     properties: [ node ],
8150                 }) : node;
8151             }));
8152         });
8153         def(AST_ObjectIdentity, return_null);
8154         def(AST_Sequence, function(compressor, first_in_statement) {
8155             var expressions = trim(this.expressions, compressor, first_in_statement);
8156             if (!expressions) return null;
8157             var end = expressions.length - 1;
8158             var last = expressions[end];
8159             if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) {
8160                 expressions = expressions.slice(0, -1);
8161                 end--;
8162                 last.expression = expressions[end];
8163                 expressions[end] = last;
8164             }
8165             var assign, cond, lhs;
8166             if (compressor.option("conditionals")
8167                 && end > 0
8168                 && (assign = expressions[end - 1]) instanceof AST_Assign
8169                 && assign.operator == "="
8170                 && (lhs = assign.left) instanceof AST_SymbolRef
8171                 && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) {
8172                 assign = assign.clone();
8173                 assign.right = cond;
8174                 expressions = expressions.slice(0, -2);
8175                 expressions.push(assign.drop_side_effect_free(compressor, first_in_statement));
8176             }
8177             return expressions === this.expressions ? this : make_sequence(this, expressions);
8178         });
8179         def(AST_Sub, function(compressor, first_in_statement) {
8180             var expr = this.expression;
8181             var prop = this.property;
8182             if (expr.may_throw_on_access(compressor)) {
8183                 if (!this.optional) return this;
8184                 if (prop.has_side_effects(compressor)) {
8185                     prop = prop.drop_side_effect_free(compressor);
8186                     if (!prop) return expr.drop_side_effect_free(compressor, first_in_statement);
8187                     var node = this.clone();
8188                     node.property = prop;
8189                     return node;
8190                 }
8191             }
8192             expr = expr.drop_side_effect_free(compressor, first_in_statement);
8193             if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement);
8194             prop = prop.drop_side_effect_free(compressor);
8195             if (!prop) return expr;
8196             return make_sequence(this, [ expr, prop ]);
8197         });
8198         def(AST_SymbolRef, function(compressor) {
8199             return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
8200         });
8201         def(AST_Template, function(compressor, first_in_statement) {
8202             var self = this;
8203             if (self.is_expr_pure(compressor)) {
8204                 var expressions = self.expressions;
8205                 if (expressions.length == 0) return null;
8206                 return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
8207             }
8208             var tag = self.tag;
8209             var dropped = drop_returns(compressor, tag);
8210             if (dropped) {
8211                 // always shallow clone to signal internal changes
8212                 self = self.clone();
8213                 self.tag = dropped;
8214                 // avoid extraneous traversal
8215                 if (tag._squeezed) self.tag._squeezed = true;
8216             }
8217             return self;
8218         });
8219         def(AST_Unary, function(compressor, first_in_statement) {
8220             var exp = this.expression;
8221             if (unary_side_effects[this.operator]) {
8222                 this.write_only = !exp.has_side_effects(compressor);
8223                 return this;
8224             }
8225             if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) {
8226                 return null;
8227             }
8228             var node = exp.drop_side_effect_free(compressor, first_in_statement);
8229             if (first_in_statement && node && is_iife_call(node)) {
8230                 if (node === exp && this.operator == "!") return this;
8231                 return node.negate(compressor, first_in_statement);
8232             }
8233             return node;
8234         });
8235     })(function(node, func) {
8236         node.DEFMETHOD("drop_side_effect_free", func);
8237     });
8238
8239     OPT(AST_SimpleStatement, function(self, compressor) {
8240         if (compressor.option("side_effects")) {
8241             var body = self.body;
8242             var node = body.drop_side_effect_free(compressor, true);
8243             if (!node) {
8244                 AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
8245                 return make_node(AST_EmptyStatement, self);
8246             }
8247             if (node !== body) {
8248                 return make_node(AST_SimpleStatement, self, { body: node });
8249             }
8250         }
8251         return self;
8252     });
8253
8254     OPT(AST_While, function(self, compressor) {
8255         return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
8256     });
8257
8258     function has_loop_control(loop, parent, type) {
8259         if (!type) type = AST_LoopControl;
8260         var found = false;
8261         var tw = new TreeWalker(function(node) {
8262             if (found || node instanceof AST_Scope) return true;
8263             if (node instanceof type && tw.loopcontrol_target(node) === loop) {
8264                 return found = true;
8265             }
8266         });
8267         if (parent instanceof AST_LabeledStatement) tw.push(parent);
8268         tw.push(loop);
8269         loop.body.walk(tw);
8270         return found;
8271     }
8272
8273     OPT(AST_Do, function(self, compressor) {
8274         if (!compressor.option("loops")) return self;
8275         var cond = fuzzy_eval(compressor, self.condition);
8276         if (!(cond instanceof AST_Node)) {
8277             if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, {
8278                 body: make_node(AST_BlockStatement, self.body, {
8279                     body: [
8280                         self.body,
8281                         make_node(AST_SimpleStatement, self.condition, {
8282                             body: self.condition
8283                         }),
8284                     ]
8285                 })
8286             }).optimize(compressor);
8287             if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, {
8288                 body: [
8289                     self.body,
8290                     make_node(AST_SimpleStatement, self.condition, {
8291                         body: self.condition
8292                     }),
8293                 ]
8294             }).optimize(compressor);
8295         }
8296         if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
8297             var body = self.body.body;
8298             for (var i = body.length; --i >= 0;) {
8299                 var stat = body[i];
8300                 if (stat instanceof AST_If
8301                     && !stat.alternative
8302                     && stat.body instanceof AST_Break
8303                     && compressor.loopcontrol_target(stat.body) === self) {
8304                     if (has_block_scope_refs(stat.condition)) break;
8305                     self.condition = make_node(AST_Binary, self, {
8306                         operator: "&&",
8307                         left: stat.condition.negate(compressor),
8308                         right: self.condition,
8309                     });
8310                     body.splice(i, 1);
8311                 } else if (stat instanceof AST_SimpleStatement) {
8312                     if (has_block_scope_refs(stat.body)) break;
8313                     self.condition = make_sequence(self, [
8314                         stat.body,
8315                         self.condition,
8316                     ]);
8317                     body.splice(i, 1);
8318                 } else if (!is_declaration(stat, true)) {
8319                     break;
8320                 }
8321             }
8322             self.body = trim_block(self.body, compressor.parent());
8323         }
8324         if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
8325         if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
8326             condition: make_sequence(self.condition, [
8327                 self.body.body,
8328                 self.condition
8329             ]),
8330             body: make_node(AST_EmptyStatement, self)
8331         }).optimize(compressor);
8332         return self;
8333
8334         function has_block_scope_refs(node) {
8335             var found = false;
8336             node.walk(new TreeWalker(function(node) {
8337                 if (found) return true;
8338                 if (node instanceof AST_SymbolRef) {
8339                     if (!member(node.definition(), self.enclosed)) found = true;
8340                     return true;
8341                 }
8342             }));
8343             return found;
8344         }
8345     });
8346
8347     function if_break_in_loop(self, compressor) {
8348         var first = first_statement(self.body);
8349         if (compressor.option("dead_code")
8350             && (first instanceof AST_Break
8351                 || first instanceof AST_Continue && external_target(first)
8352                 || first instanceof AST_Exit)) {
8353             var body = [];
8354             if (is_statement(self.init)) {
8355                 body.push(self.init);
8356             } else if (self.init) {
8357                 body.push(make_node(AST_SimpleStatement, self.init, {
8358                     body: self.init
8359                 }));
8360             }
8361             var retain = external_target(first) || first instanceof AST_Exit;
8362             if (self.condition && retain) {
8363                 body.push(make_node(AST_If, self, {
8364                     condition: self.condition,
8365                     body: first,
8366                     alternative: null
8367                 }));
8368             } else if (self.condition) {
8369                 body.push(make_node(AST_SimpleStatement, self.condition, {
8370                     body: self.condition
8371                 }));
8372             } else if (retain) {
8373                 body.push(first);
8374             }
8375             extract_declarations_from_unreachable_code(compressor, self.body, body);
8376             return make_node(AST_BlockStatement, self, {
8377                 body: body
8378             });
8379         }
8380         if (first instanceof AST_If) {
8381             var ab = first_statement(first.body);
8382             if (ab instanceof AST_Break && !external_target(ab)) {
8383                 if (self.condition) {
8384                     self.condition = make_node(AST_Binary, self.condition, {
8385                         left: self.condition,
8386                         operator: "&&",
8387                         right: first.condition.negate(compressor),
8388                     });
8389                 } else {
8390                     self.condition = first.condition.negate(compressor);
8391                 }
8392                 var body = as_statement_array(first.alternative);
8393                 extract_declarations_from_unreachable_code(compressor, first.body, body);
8394                 return drop_it(body);
8395             }
8396             ab = first_statement(first.alternative);
8397             if (ab instanceof AST_Break && !external_target(ab)) {
8398                 if (self.condition) {
8399                     self.condition = make_node(AST_Binary, self.condition, {
8400                         left: self.condition,
8401                         operator: "&&",
8402                         right: first.condition,
8403                     });
8404                 } else {
8405                     self.condition = first.condition;
8406                 }
8407                 var body = as_statement_array(first.body);
8408                 extract_declarations_from_unreachable_code(compressor, first.alternative, body);
8409                 return drop_it(body);
8410             }
8411         }
8412         return self;
8413
8414         function first_statement(body) {
8415             return body instanceof AST_BlockStatement ? body.body[0] : body;
8416         }
8417
8418         function external_target(node) {
8419             return compressor.loopcontrol_target(node) !== compressor.self();
8420         }
8421
8422         function drop_it(rest) {
8423             if (self.body instanceof AST_BlockStatement) {
8424                 self.body = self.body.clone();
8425                 self.body.body = rest.concat(self.body.body.slice(1));
8426                 self.body = self.body.transform(compressor);
8427             } else {
8428                 self.body = make_node(AST_BlockStatement, self.body, {
8429                     body: rest
8430                 }).transform(compressor);
8431             }
8432             return if_break_in_loop(self, compressor);
8433         }
8434     }
8435
8436     OPT(AST_For, function(self, compressor) {
8437         if (!compressor.option("loops")) return self;
8438         if (compressor.option("side_effects")) {
8439             if (self.init) self.init = self.init.drop_side_effect_free(compressor);
8440             if (self.step) self.step = self.step.drop_side_effect_free(compressor);
8441         }
8442         if (self.condition) {
8443             var cond = fuzzy_eval(compressor, self.condition);
8444             if (!cond) {
8445                 if (compressor.option("dead_code")) {
8446                     var body = [];
8447                     if (is_statement(self.init)) {
8448                         body.push(self.init);
8449                     } else if (self.init) {
8450                         body.push(make_node(AST_SimpleStatement, self.init, { body: self.init }));
8451                     }
8452                     body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition }));
8453                     extract_declarations_from_unreachable_code(compressor, self.body, body);
8454                     return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8455                 }
8456             } else if (!(cond instanceof AST_Node)) {
8457                 self.body = make_node(AST_BlockStatement, self.body, {
8458                     body: [
8459                         make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8460                         self.body,
8461                     ],
8462                 });
8463                 self.condition = null;
8464             }
8465         }
8466         return if_break_in_loop(self, compressor);
8467     });
8468
8469     OPT(AST_ForEnumeration, function(self, compressor) {
8470         if (compressor.option("varify") && is_lexical_definition(self.init)) {
8471             var name = self.init.definitions[0].name;
8472             if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
8473                 && !name.match_symbol(function(node) {
8474                     if (node instanceof AST_SymbolDeclaration) {
8475                         var def = node.definition();
8476                         return !same_scope(def) || may_overlap(compressor, def);
8477                     }
8478                 }, true)) {
8479                 self.init = to_var(self.init);
8480             }
8481         }
8482         return self;
8483     });
8484
8485     function mark_locally_defined(condition, consequent, alternative) {
8486         if (!(condition instanceof AST_Binary)) return;
8487         if (!(condition.left instanceof AST_String)) {
8488             switch (condition.operator) {
8489               case "&&":
8490                 mark_locally_defined(condition.left, consequent);
8491                 mark_locally_defined(condition.right, consequent);
8492                 break;
8493               case "||":
8494                 mark_locally_defined(negate(condition.left), alternative);
8495                 mark_locally_defined(negate(condition.right), alternative);
8496                 break;
8497             }
8498             return;
8499         }
8500         if (!(condition.right instanceof AST_UnaryPrefix)) return;
8501         if (condition.right.operator != "typeof") return;
8502         var sym = condition.right.expression;
8503         if (!is_undeclared_ref(sym)) return;
8504         var body;
8505         var undef = condition.left.value == "undefined";
8506         switch (condition.operator) {
8507           case "==":
8508             body = undef ? alternative : consequent;
8509             break;
8510           case "!=":
8511             body = undef ? consequent : alternative;
8512             break;
8513           default:
8514             return;
8515         }
8516         if (!body) return;
8517         var def = sym.definition();
8518         var tw = new TreeWalker(function(node) {
8519             if (node instanceof AST_Scope) {
8520                 var parent = tw.parent();
8521                 if (parent instanceof AST_Call && parent.expression === node) return;
8522                 return true;
8523             }
8524             if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
8525         });
8526         body.walk(tw);
8527
8528         function negate(node) {
8529             if (!(node instanceof AST_Binary)) return;
8530             switch (node.operator) {
8531               case "==":
8532                 node = node.clone();
8533                 node.operator = "!=";
8534                 return node;
8535               case "!=":
8536                 node = node.clone();
8537                 node.operator = "==";
8538                 return node;
8539             }
8540         }
8541     }
8542
8543     function fuzzy_eval(compressor, node, nullish) {
8544         if (node.truthy) return true;
8545         if (node.falsy && !nullish) return false;
8546         if (node.is_truthy()) return true;
8547         return node.evaluate(compressor, true);
8548     }
8549
8550     function mark_duplicate_condition(compressor, node) {
8551         var child;
8552         var level = 0;
8553         var negated = false;
8554         var parent = compressor.self();
8555         if (!is_statement(parent)) while (true) {
8556             child = parent;
8557             parent = compressor.parent(level++);
8558             if (parent instanceof AST_Binary) {
8559                 var op = parent.operator;
8560                 if (!lazy_op[op]) return;
8561                 var left = parent.left;
8562                 if (left === child) continue;
8563                 if (match(left)) switch (op) {
8564                   case "&&":
8565                     node[negated ? "falsy" : "truthy"] = true;
8566                     break;
8567                   case "||":
8568                   case "??":
8569                     node[negated ? "truthy" : "falsy"] = true;
8570                     break;
8571                 }
8572             } else if (parent instanceof AST_Conditional) {
8573                 var cond = parent.condition;
8574                 if (cond === child) continue;
8575                 if (match(cond)) switch (child) {
8576                   case parent.consequent:
8577                     node[negated ? "falsy" : "truthy"] = true;
8578                     break;
8579                   case parent.alternative:
8580                     node[negated ? "truthy" : "falsy"] = true;
8581                     break;
8582                 }
8583             } else if (parent instanceof AST_Exit) {
8584                 break;
8585             } else if (parent instanceof AST_If) {
8586                 break;
8587             } else if (parent instanceof AST_Sequence) {
8588                 if (parent.expressions[0] === child) continue;
8589             } else if (parent instanceof AST_SimpleStatement) {
8590                 break;
8591             }
8592             return;
8593         }
8594         while (true) {
8595             child = parent;
8596             parent = compressor.parent(level++);
8597             if (parent instanceof AST_BlockStatement) {
8598                 if (parent.body[0] === child) continue;
8599             } else if (parent instanceof AST_If) {
8600                 if (match(parent.condition)) switch (child) {
8601                   case parent.body:
8602                     node[negated ? "falsy" : "truthy"] = true;
8603                     break;
8604                   case parent.alternative:
8605                     node[negated ? "truthy" : "falsy"] = true;
8606                     break;
8607                 }
8608             }
8609             return;
8610         }
8611
8612         function match(cond) {
8613             if (node.equivalent_to(cond)) return true;
8614             if (!(cond instanceof AST_UnaryPrefix)) return false;
8615             if (cond.operator != "!") return false;
8616             if (!node.equivalent_to(cond.expression)) return false;
8617             negated = true;
8618             return true;
8619         }
8620     }
8621
8622     OPT(AST_If, function(self, compressor) {
8623         if (is_empty(self.alternative)) self.alternative = null;
8624
8625         if (!compressor.option("conditionals")) return self;
8626         if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) {
8627             mark_duplicate_condition(compressor, self.condition);
8628         }
8629         // if condition can be statically determined, warn and drop
8630         // one of the blocks.  note, statically determined implies
8631         // “has no side effects”; also it doesn't work for cases like
8632         // `x && true`, though it probably should.
8633         if (compressor.option("dead_code")) {
8634             var cond = fuzzy_eval(compressor, self.condition);
8635             if (!cond) {
8636                 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
8637                 var body = [ make_node(AST_SimpleStatement, self.condition, { body: self.condition }) ];
8638                 extract_declarations_from_unreachable_code(compressor, self.body, body);
8639                 if (self.alternative) body.push(self.alternative);
8640                 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8641             } else if (!(cond instanceof AST_Node)) {
8642                 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
8643                 var body = [
8644                     make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8645                     self.body,
8646                 ];
8647                 if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body);
8648                 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8649             }
8650         }
8651         var negated = self.condition.negate(compressor);
8652         var self_condition_length = self.condition.print_to_string().length;
8653         var negated_length = negated.print_to_string().length;
8654         var negated_is_best = negated_length < self_condition_length;
8655         if (self.alternative && negated_is_best) {
8656             negated_is_best = false; // because we already do the switch here.
8657             // no need to swap values of self_condition_length and negated_length
8658             // here because they are only used in an equality comparison later on.
8659             self.condition = negated;
8660             var tmp = self.body;
8661             self.body = self.alternative || make_node(AST_EmptyStatement, self);
8662             self.alternative = tmp;
8663         }
8664         var body = [], var_defs = [], refs = [];
8665         var body_exprs = sequencesize(self.body, body, var_defs, refs);
8666         var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
8667         if (body_exprs && alt_exprs) {
8668             if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
8669             if (body_exprs.length == 0) {
8670                 body.push(make_node(AST_SimpleStatement, self.condition, {
8671                     body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
8672                         operator : "||",
8673                         left     : self.condition,
8674                         right    : make_sequence(self.alternative, alt_exprs)
8675                     }).transform(compressor) : self.condition.clone()
8676                 }).optimize(compressor));
8677             } else if (alt_exprs.length == 0) {
8678                 if (self_condition_length === negated_length && !negated_is_best
8679                     && self.condition instanceof AST_Binary && self.condition.operator == "||") {
8680                     // although the code length of self.condition and negated are the same,
8681                     // negated does not require additional surrounding parentheses.
8682                     // see https://github.com/mishoo/UglifyJS/issues/979
8683                     negated_is_best = true;
8684                 }
8685                 body.push(make_node(AST_SimpleStatement, self, {
8686                     body: make_node(AST_Binary, self, {
8687                         operator : negated_is_best ? "||" : "&&",
8688                         left     : negated_is_best ? negated : self.condition,
8689                         right    : make_sequence(self.body, body_exprs)
8690                     }).transform(compressor)
8691                 }).optimize(compressor));
8692             } else {
8693                 body.push(make_node(AST_SimpleStatement, self, {
8694                     body: make_node(AST_Conditional, self, {
8695                         condition   : self.condition,
8696                         consequent  : make_sequence(self.body, body_exprs),
8697                         alternative : make_sequence(self.alternative, alt_exprs)
8698                     })
8699                 }).optimize(compressor));
8700             }
8701             refs.forEach(function(ref) {
8702                 ref.definition().references.push(ref);
8703             });
8704             return make_node(AST_BlockStatement, self, {
8705                 body: body
8706             }).optimize(compressor);
8707         }
8708         if (is_empty(self.body)) {
8709             self = make_node(AST_If, self, {
8710                 condition: negated,
8711                 body: self.alternative,
8712                 alternative: null
8713             });
8714         }
8715         if (self.body instanceof AST_Exit
8716             && self.alternative instanceof AST_Exit
8717             && self.body.TYPE == self.alternative.TYPE) {
8718             var exit = make_node(self.body.CTOR, self, {
8719                 value: make_node(AST_Conditional, self, {
8720                     condition   : self.condition,
8721                     consequent  : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
8722                     alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
8723                 })
8724             });
8725             if (exit instanceof AST_Return) {
8726                 exit.in_bool = self.body.in_bool || self.alternative.in_bool;
8727             }
8728             return exit;
8729         }
8730         if (self.body instanceof AST_If
8731             && !self.body.alternative
8732             && !self.alternative) {
8733             self = make_node(AST_If, self, {
8734                 condition: make_node(AST_Binary, self.condition, {
8735                     operator: "&&",
8736                     left: self.condition,
8737                     right: self.body.condition
8738                 }),
8739                 body: self.body.body,
8740                 alternative: null
8741             });
8742         }
8743         if (aborts(self.body)) {
8744             if (self.alternative) {
8745                 var alt = self.alternative;
8746                 self.alternative = null;
8747                 return make_node(AST_BlockStatement, self, {
8748                     body: [ self, alt ]
8749                 }).optimize(compressor);
8750             }
8751         }
8752         if (aborts(self.alternative)) {
8753             var body = self.body;
8754             self.body = self.alternative;
8755             self.condition = negated_is_best ? negated : self.condition.negate(compressor);
8756             self.alternative = null;
8757             return make_node(AST_BlockStatement, self, {
8758                 body: [ self, body ]
8759             }).optimize(compressor);
8760         }
8761         if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
8762         return self;
8763
8764         function sequencesize(stat, defuns, var_defs, refs) {
8765             if (stat == null) return [];
8766             if (stat instanceof AST_BlockStatement) {
8767                 var exprs = [];
8768                 for (var i = 0; i < stat.body.length; i++) {
8769                     var line = stat.body[i];
8770                     if (line instanceof AST_LambdaDefinition) {
8771                         defuns.push(line);
8772                     } else if (line instanceof AST_EmptyStatement) {
8773                         continue;
8774                     } else if (line instanceof AST_SimpleStatement) {
8775                         if (!compressor.option("sequences") && exprs.length > 0) return;
8776                         exprs.push(line.body);
8777                     } else if (line instanceof AST_Var) {
8778                         if (!compressor.option("sequences") && exprs.length > 0) return;
8779                         line.remove_initializers(compressor, var_defs);
8780                         line.definitions.forEach(process_var_def);
8781                     } else {
8782                         return;
8783                     }
8784                 }
8785                 return exprs;
8786             }
8787             if (stat instanceof AST_LambdaDefinition) {
8788                 defuns.push(stat);
8789                 return [];
8790             }
8791             if (stat instanceof AST_EmptyStatement) return [];
8792             if (stat instanceof AST_SimpleStatement) return [ stat.body ];
8793             if (stat instanceof AST_Var) {
8794                 var exprs = [];
8795                 stat.remove_initializers(compressor, var_defs);
8796                 stat.definitions.forEach(process_var_def);
8797                 return exprs;
8798             }
8799
8800             function process_var_def(var_def) {
8801                 if (!var_def.value) return;
8802                 exprs.push(make_node(AST_Assign, var_def, {
8803                     operator: "=",
8804                     left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) {
8805                         refs.push(ref);
8806                     }),
8807                     right: var_def.value
8808                 }));
8809             }
8810         }
8811     });
8812
8813     OPT(AST_Switch, function(self, compressor) {
8814         if (!compressor.option("switches")) return self;
8815         if (!compressor.option("dead_code")) return self;
8816         var body = [];
8817         var branch;
8818         var decl = [];
8819         var default_branch;
8820         var exact_match;
8821         var side_effects = [];
8822         for (var i = 0, len = self.body.length; i < len; i++) {
8823             branch = self.body[i];
8824             if (branch instanceof AST_Default) {
8825                 var prev = body[body.length - 1];
8826                 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
8827                     eliminate_branch(branch, prev);
8828                     continue;
8829                 } else {
8830                     default_branch = branch;
8831                 }
8832             } else {
8833                 var exp = branch.expression;
8834                 var equals = make_node(AST_Binary, self, {
8835                     operator: "===",
8836                     left: self.expression,
8837                     right: exp,
8838                 }).evaluate(compressor, true);
8839                 if (!equals) {
8840                     if (exp.has_side_effects(compressor)) side_effects.push(exp);
8841                     eliminate_branch(branch, body[body.length - 1]);
8842                     continue;
8843                 }
8844                 if (!(equals instanceof AST_Node)) {
8845                     if (default_branch) {
8846                         var default_index = body.indexOf(default_branch);
8847                         body.splice(default_index, 1);
8848                         eliminate_branch(default_branch, body[default_index - 1]);
8849                         default_branch = null;
8850                     }
8851                     if (exp.has_side_effects(compressor)) {
8852                         exact_match = branch;
8853                     } else {
8854                         default_branch = branch = make_node(AST_Default, branch, branch);
8855                     }
8856                     while (++i < len) eliminate_branch(self.body[i], branch);
8857                 }
8858             }
8859             if (i + 1 >= len || aborts(branch)) {
8860                 var prev = body[body.length - 1];
8861                 var statements = branch.body;
8862                 if (aborts(prev)) switch (prev.body.length - statements.length) {
8863                   case 1:
8864                     var stat = prev.body[prev.body.length - 1];
8865                     if (!is_break(stat, compressor)) break;
8866                     statements = statements.concat(stat);
8867                   case 0:
8868                     var prev_block = make_node(AST_BlockStatement, prev, prev);
8869                     var next_block = make_node(AST_BlockStatement, branch, { body: statements });
8870                     if (prev_block.equivalent_to(next_block)) prev.body = [];
8871                 }
8872             }
8873             if (side_effects.length) {
8874                 if (branch instanceof AST_Default) {
8875                     body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8876                 } else {
8877                     side_effects.push(branch.expression);
8878                     branch.expression = make_sequence(self, side_effects);
8879                 }
8880                 side_effects = [];
8881             }
8882             body.push(branch);
8883         }
8884         if (side_effects.length && !exact_match) {
8885             body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8886         }
8887         while (branch = body[body.length - 1]) {
8888             var stat = branch.body[branch.body.length - 1];
8889             if (is_break(stat, compressor)) branch.body.pop();
8890             if (branch === default_branch) {
8891                 if (!has_declarations_only(branch)) break;
8892             } else if (branch.expression.has_side_effects(compressor)) {
8893                 break;
8894             } else if (default_branch) {
8895                 if (!has_declarations_only(default_branch)) break;
8896                 if (body[body.length - 2] !== default_branch) break;
8897                 default_branch.body = default_branch.body.concat(branch.body);
8898                 branch.body = [];
8899             } else if (!has_declarations_only(branch)) break;
8900             eliminate_branch(branch);
8901             if (body.pop() === default_branch) default_branch = null;
8902         }
8903         if (!branch) {
8904             decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
8905             if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, {
8906                 body: make_sequence(self, side_effects),
8907             }));
8908             return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor);
8909         }
8910         if (branch === default_branch) while (branch = body[body.length - 2]) {
8911             if (branch instanceof AST_Default) break;
8912             if (!has_declarations_only(branch)) break;
8913             var exp = branch.expression;
8914             if (exp.has_side_effects(compressor)) {
8915                 var prev = body[body.length - 3];
8916                 if (prev && !aborts(prev)) break;
8917                 default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp }));
8918             }
8919             eliminate_branch(branch);
8920             body.splice(-2, 1);
8921         }
8922         body[0].body = decl.concat(body[0].body);
8923         self.body = body;
8924         if (compressor.option("conditionals")) switch (body.length) {
8925           case 1:
8926             if (!no_break(body[0])) break;
8927             var exp = body[0].expression;
8928             var statements = body[0].body.slice();
8929             if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
8930                 condition: make_node(AST_Binary, self, {
8931                     operator: "===",
8932                     left: self.expression,
8933                     right: exp,
8934                 }),
8935                 body: make_node(AST_BlockStatement, self, {
8936                     body: statements,
8937                 }),
8938                 alternative: null,
8939             }).optimize(compressor);
8940             if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
8941                 body: exp,
8942             }));
8943             statements.unshift(make_node(AST_SimpleStatement, self.expression, {
8944                 body:self.expression,
8945             }));
8946             return make_node(AST_BlockStatement, self, {
8947                 body: statements,
8948             }).optimize(compressor);
8949           case 2:
8950             if (!member(default_branch, body) || !no_break(body[1])) break;
8951             var statements = body[0].body.slice();
8952             var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
8953             if (exclusive) statements.pop();
8954             if (!all(statements, no_break)) break;
8955             var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1], body[1]);
8956             var node = make_node(AST_If, self, {
8957                 condition: make_node(AST_Binary, self, body[0] === default_branch ? {
8958                     operator: "!==",
8959                     left: self.expression,
8960                     right: body[1].expression,
8961                 } : {
8962                     operator: "===",
8963                     left: self.expression,
8964                     right: body[0].expression,
8965                 }),
8966                 body: make_node(AST_BlockStatement, body[0], {
8967                     body: statements,
8968                 }),
8969                 alternative: exclusive && alternative || null,
8970             });
8971             if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, {
8972                 body: [ node, alternative ],
8973             });
8974             return node.optimize(compressor);
8975         }
8976         return self;
8977
8978         function is_break(node, tw) {
8979             return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
8980         }
8981
8982         function no_break(node) {
8983             var found = false;
8984             var tw = new TreeWalker(function(node) {
8985                 if (found
8986                     || node instanceof AST_Lambda
8987                     || node instanceof AST_SimpleStatement) return true;
8988                 if (is_break(node, tw)) found = true;
8989             });
8990             tw.push(self);
8991             node.walk(tw);
8992             return !found;
8993         }
8994
8995         function eliminate_branch(branch, prev) {
8996             if (prev && !aborts(prev)) {
8997                 prev.body = prev.body.concat(branch.body);
8998             } else {
8999                 extract_declarations_from_unreachable_code(compressor, branch, decl);
9000             }
9001         }
9002     });
9003
9004     OPT(AST_Try, function(self, compressor) {
9005         self.body = tighten_body(self.body, compressor);
9006         if (compressor.option("dead_code")) {
9007             if (has_declarations_only(self)
9008                 && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
9009                     return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
9010                 }, true))) {
9011                 var body = [];
9012                 if (self.bcatch) {
9013                     extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
9014                     body.forEach(function(stat) {
9015                         if (!(stat instanceof AST_Var)) return;
9016                         stat.definitions.forEach(function(var_def) {
9017                             var def = var_def.name.definition().redefined();
9018                             if (!def) return;
9019                             var_def.name = var_def.name.clone();
9020                             var_def.name.thedef = def;
9021                         });
9022                     });
9023                 }
9024                 body.unshift(make_node(AST_BlockStatement, self, self).optimize(compressor));
9025                 if (self.bfinally) {
9026                     body.push(make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor));
9027                 }
9028                 return make_node(AST_BlockStatement, self, {
9029                     body: body
9030                 }).optimize(compressor);
9031             }
9032             if (self.bfinally && has_declarations_only(self.bfinally)) {
9033                 var body = make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor);
9034                 body = self.body.concat(body);
9035                 if (!self.bcatch) return make_node(AST_BlockStatement, self, {
9036                     body: body
9037                 }).optimize(compressor);
9038                 self.body = body;
9039                 self.bfinally = null;
9040             }
9041         }
9042         return self;
9043     });
9044
9045     function remove_initializers(make_value) {
9046         return function(compressor, defns) {
9047             var dropped = false;
9048             this.definitions.forEach(function(defn) {
9049                 if (defn.value) dropped = true;
9050                 defn.name.match_symbol(function(node) {
9051                     if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, {
9052                         name: node,
9053                         value: make_value(compressor, node)
9054                     }));
9055                 }, true);
9056             });
9057             return dropped;
9058         };
9059     }
9060
9061     AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) {
9062         return make_node(AST_Undefined, node).optimize(compressor);
9063     }));
9064     AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9065     AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9066
9067     AST_Definitions.DEFMETHOD("to_assignments", function() {
9068         var assignments = this.definitions.reduce(function(a, defn) {
9069             var def = defn.name.definition();
9070             var value = defn.value;
9071             if (value) {
9072                 if (value instanceof AST_Sequence) value = value.clone();
9073                 var name = make_node(AST_SymbolRef, defn.name, defn.name);
9074                 var assign = make_node(AST_Assign, defn, {
9075                     operator: "=",
9076                     left: name,
9077                     right: value,
9078                 });
9079                 a.push(assign);
9080                 var fixed = function() {
9081                     return assign.right;
9082                 };
9083                 fixed.assigns = [ assign ];
9084                 fixed.direct_access = def.direct_access;
9085                 fixed.escaped = def.escaped;
9086                 name.fixed = fixed;
9087                 def.references.forEach(function(ref) {
9088                     var assigns = ref.fixed && ref.fixed.assigns;
9089                     if (assigns && assigns[0] === defn) assigns[0] = assign;
9090                 });
9091                 def.references.push(name);
9092             }
9093             def.eliminated++;
9094             def.single_use = false;
9095             return a;
9096         }, []);
9097         if (assignments.length == 0) return null;
9098         return make_sequence(this, assignments);
9099     });
9100
9101     function is_safe_lexical(def) {
9102         return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
9103     }
9104
9105     function may_overlap(compressor, def) {
9106         if (compressor.exposed(def)) return true;
9107         var scope = def.scope.resolve();
9108         for (var s = def.scope; s !== scope;) {
9109             s = s.parent_scope;
9110             if (s.var_names()[def.name]) return true;
9111         }
9112     }
9113
9114     function to_var(stat) {
9115         return make_node(AST_Var, stat, {
9116             definitions: stat.definitions.map(function(defn) {
9117                 return make_node(AST_VarDef, defn, {
9118                     name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
9119                         var def = name.definition();
9120                         def.orig[def.orig.indexOf(node)] = name;
9121                         var scope = def.scope.resolve();
9122                         if (def.scope === scope) return;
9123                         def.scope = scope;
9124                         scope.variables.set(def.name, def);
9125                         scope.enclosed.push(def);
9126                         scope.var_names()[def.name] = true;
9127                     }),
9128                     value: defn.value,
9129                 });
9130             })
9131         });
9132     }
9133
9134     function can_varify(compressor, sym) {
9135         if (!sym.fixed_value()) return false;
9136         var def = sym.definition();
9137         return is_safe_lexical(def) && same_scope(def) && !may_overlap(compressor, def);
9138     }
9139
9140     function varify(self, compressor) {
9141         return compressor.option("varify") && all(self.definitions, function(defn) {
9142             return !defn.name.match_symbol(function(node) {
9143                 if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
9144             }, true);
9145         }) ? to_var(self) : self;
9146     }
9147
9148     OPT(AST_Const, varify);
9149     OPT(AST_Let, varify);
9150
9151     function trim_optional_chain(node, compressor) {
9152         if (!compressor.option("optional_chains")) return;
9153         if (node.terminal) do {
9154             var expr = node.expression;
9155             if (node.optional) {
9156                 var ev = fuzzy_eval(compressor, expr, true);
9157                 if (ev == null) return make_node(AST_UnaryPrefix, node, {
9158                     operator: "void",
9159                     expression: expr,
9160                 }).optimize(compressor);
9161                 if (!(ev instanceof AST_Node)) node.optional = false;
9162             }
9163             node = expr;
9164         } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal);
9165     }
9166
9167     function lift_sequence_in_expression(node, compressor) {
9168         var exp = node.expression;
9169         if (!(exp instanceof AST_Sequence)) return node;
9170         var x = exp.expressions.slice();
9171         var e = node.clone();
9172         e.expression = x.pop();
9173         x.push(e);
9174         return make_sequence(node, x);
9175     }
9176
9177     function drop_unused_call_args(call, compressor, fns_with_marked_args) {
9178         var exp = call.expression;
9179         var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9180         if (!(fn instanceof AST_Lambda)) return;
9181         if (fn.uses_arguments) return;
9182         if (fn.pinned()) return;
9183         if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
9184         var args = call.args;
9185         if (!all(args, function(arg) {
9186             return !(arg instanceof AST_Spread);
9187         })) return;
9188         var argnames = fn.argnames;
9189         var is_iife = fn === exp && !fn.name;
9190         if (fn.rest) {
9191             if (!(is_iife && compressor.option("rests"))) return;
9192             var insert = argnames.length;
9193             args = args.slice(0, insert);
9194             while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor));
9195             args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) }));
9196             argnames = argnames.concat(fn.rest);
9197             fn.rest = null;
9198         } else {
9199             args = args.slice();
9200             argnames = argnames.slice();
9201         }
9202         var pos = 0, last = 0;
9203         var drop_defaults = is_iife && compressor.option("default_values");
9204         var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
9205             if (!argname) return true;
9206             if (argname instanceof AST_DestructuredArray) {
9207                 return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
9208             }
9209             if (argname instanceof AST_DestructuredObject) {
9210                 return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
9211             }
9212             return argname.__unused;
9213         } : return_false;
9214         var side_effects = [];
9215         for (var i = 0; i < args.length; i++) {
9216             var argname = argnames[i];
9217             if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
9218                 argnames[i] = argname = argname.name;
9219             }
9220             if (!argname || "__unused" in argname) {
9221                 var node = args[i].drop_side_effect_free(compressor);
9222                 if (drop_fargs(argname)) {
9223                     if (argname) argnames.splice(i, 1);
9224                     args.splice(i, 1);
9225                     if (node) side_effects.push(node);
9226                     i--;
9227                     continue;
9228                 } else if (node) {
9229                     side_effects.push(node);
9230                     args[pos++] = make_sequence(call, side_effects);
9231                     side_effects = [];
9232                 } else if (argname) {
9233                     if (side_effects.length) {
9234                         args[pos++] = make_sequence(call, side_effects);
9235                         side_effects = [];
9236                     } else {
9237                         args[pos++] = make_node(AST_Number, args[i], {
9238                             value: 0
9239                         });
9240                         continue;
9241                     }
9242                 }
9243             } else if (drop_fargs(argname, args[i])) {
9244                 var node = args[i].drop_side_effect_free(compressor);
9245                 argnames.splice(i, 1);
9246                 args.splice(i, 1);
9247                 if (node) side_effects.push(node);
9248                 i--;
9249                 continue;
9250             } else {
9251                 side_effects.push(args[i]);
9252                 args[pos++] = make_sequence(call, side_effects);
9253                 side_effects = [];
9254             }
9255             last = pos;
9256         }
9257         for (; i < argnames.length; i++) {
9258             if (drop_fargs(argnames[i])) argnames.splice(i--, 1);
9259         }
9260         fn.argnames = argnames;
9261         args.length = last;
9262         call.args = args;
9263         if (!side_effects.length) return;
9264         var arg = make_sequence(call, side_effects);
9265         args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, {
9266             operator: "void",
9267             expression: arg,
9268         }) : arg);
9269     }
9270
9271     function avoid_await_yield(parent_scope) {
9272         var avoid = [];
9273         if (is_async(parent_scope)) avoid.push("await");
9274         if (is_generator(parent_scope)) avoid.push("yield");
9275         return avoid.length && makePredicate(avoid);
9276     }
9277
9278     OPT(AST_Call, function(self, compressor) {
9279         var exp = self.expression;
9280         var terminated = trim_optional_chain(self, compressor);
9281         if (terminated) return terminated;
9282         if (compressor.option("sequences")) {
9283             if (exp instanceof AST_PropAccess) {
9284                 var seq = lift_sequence_in_expression(exp, compressor);
9285                 if (seq !== exp) {
9286                     var call = self.clone();
9287                     call.expression = seq.expressions.pop();
9288                     seq.expressions.push(call);
9289                     return seq.optimize(compressor);
9290                 }
9291             } else if (!needs_unbinding(compressor, exp.tail_node())) {
9292                 var seq = lift_sequence_in_expression(self, compressor);
9293                 if (seq !== self) return seq.optimize(compressor);
9294             }
9295         }
9296         if (compressor.option("unused")) drop_unused_call_args(self, compressor);
9297         if (compressor.option("unsafe")) {
9298             if (is_undeclared_ref(exp)) switch (exp.name) {
9299               case "Array":
9300                 // Array(n) ---> [ , , ... , ]
9301                 if (self.args.length == 1) {
9302                     var first = self.args[0];
9303                     if (first instanceof AST_Number) try {
9304                         var length = first.value;
9305                         if (length > 6) break;
9306                         var elements = Array(length);
9307                         for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
9308                         return make_node(AST_Array, self, { elements: elements });
9309                     } catch (ex) {
9310                         AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
9311                             length: length,
9312                             file: self.start.file,
9313                             line: self.start.line,
9314                             col: self.start.col,
9315                         });
9316                         break;
9317                     }
9318                     if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
9319                 }
9320                 // Array(...) ---> [ ... ]
9321                 return make_node(AST_Array, self, { elements: self.args });
9322               case "Object":
9323                 // Object() ---> {}
9324                 if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
9325                 break;
9326               case "String":
9327                 // String() ---> ""
9328                 if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
9329                 // String(x) ---> "" + x
9330                 if (self.args.length == 1) return make_node(AST_Binary, self, {
9331                     operator: "+",
9332                     left: make_node(AST_String, self, { value: "" }),
9333                     right: self.args[0],
9334                 }).optimize(compressor);
9335                 break;
9336               case "Number":
9337                 // Number() ---> 0
9338                 if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
9339                 // Number(x) ---> +("" + x)
9340                 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9341                     operator: "+",
9342                     expression: make_node(AST_Binary, self, {
9343                         operator: "+",
9344                         left: make_node(AST_String, self, { value: "" }),
9345                         right: self.args[0],
9346                     }),
9347                 }).optimize(compressor);
9348                 break;
9349               case "Boolean":
9350                 // Boolean() ---> false
9351                 if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
9352                 // Boolean(x) ---> !!x
9353                 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9354                     operator: "!",
9355                     expression: make_node(AST_UnaryPrefix, self, {
9356                         operator: "!",
9357                         expression: self.args[0],
9358                     }),
9359                 }).optimize(compressor);
9360                 break;
9361               case "RegExp":
9362                 // attempt to convert RegExp(...) to literal
9363                 var params = [];
9364                 if (all(self.args, function(arg) {
9365                     var value = arg.evaluate(compressor);
9366                     params.unshift(value);
9367                     return arg !== value;
9368                 })) try {
9369                     return best_of(compressor, self, make_node(AST_RegExp, self, {
9370                         value: RegExp.apply(RegExp, params),
9371                     }));
9372                 } catch (ex) {
9373                     AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
9374                         expr: self,
9375                         file: self.start.file,
9376                         line: self.start.line,
9377                         col: self.start.col,
9378                     });
9379                 }
9380                 break;
9381             } else if (exp instanceof AST_Dot) switch (exp.property) {
9382               case "toString":
9383                 // x.toString() ---> "" + x
9384                 var expr = exp.expression;
9385                 if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
9386                     return make_node(AST_Binary, self, {
9387                         operator: "+",
9388                         left: make_node(AST_String, self, { value: "" }),
9389                         right: expr,
9390                     }).optimize(compressor);
9391                 }
9392                 break;
9393               case "join":
9394                 if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
9395                     var separator = self.args[0];
9396                     // [].join() ---> ""
9397                     // [].join(x) ---> (x, "")
9398                     if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
9399                         return separator ? make_sequence(self, [
9400                             separator,
9401                             make_node(AST_String, self, { value: "" }),
9402                         ]).optimize(compressor) : make_node(AST_String, self, { value: "" });
9403                     }
9404                     if (separator) {
9405                         separator = separator.evaluate(compressor);
9406                         if (separator instanceof AST_Node) break EXIT; // not a constant
9407                     }
9408                     var elements = [];
9409                     var consts = [];
9410                     for (var i = 0; i < exp.expression.elements.length; i++) {
9411                         var el = exp.expression.elements[i];
9412                         var value = el.evaluate(compressor);
9413                         if (value !== el) {
9414                             consts.push(value);
9415                         } else if (el instanceof AST_Spread) {
9416                             break EXIT;
9417                         } else {
9418                             if (consts.length > 0) {
9419                                 elements.push(make_node(AST_String, self, {
9420                                     value: consts.join(separator),
9421                                 }));
9422                                 consts.length = 0;
9423                             }
9424                             elements.push(el);
9425                         }
9426                     }
9427                     if (consts.length > 0) elements.push(make_node(AST_String, self, {
9428                         value: consts.join(separator),
9429                     }));
9430                     // [ x ].join() ---> "" + x
9431                     // [ x ].join(".") ---> "" + x
9432                     // [ 1, 2, 3 ].join() ---> "1,2,3"
9433                     // [ 1, 2, 3 ].join(".") ---> "1.2.3"
9434                     if (elements.length == 1) {
9435                         if (elements[0].is_string(compressor)) return elements[0];
9436                         return make_node(AST_Binary, elements[0], {
9437                             operator: "+",
9438                             left: make_node(AST_String, self, { value: "" }),
9439                             right: elements[0],
9440                         });
9441                     }
9442                     // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
9443                     if (separator == "") {
9444                         var first;
9445                         if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
9446                             first = elements.shift();
9447                         } else {
9448                             first = make_node(AST_String, self, { value: "" });
9449                         }
9450                         return elements.reduce(function(prev, el) {
9451                             return make_node(AST_Binary, el, {
9452                                 operator: "+",
9453                                 left: prev,
9454                                 right: el,
9455                             });
9456                         }, first).optimize(compressor);
9457                     }
9458                     // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
9459                     // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
9460                     // need this awkward cloning to not affect original element
9461                     // best_of will decide which one to get through.
9462                     var node = self.clone();
9463                     node.expression = node.expression.clone();
9464                     node.expression.expression = node.expression.expression.clone();
9465                     node.expression.expression.elements = elements;
9466                     return best_of(compressor, self, node);
9467                 }
9468                 break;
9469               case "charAt":
9470                 if (self.args.length < 2) {
9471                     var node = make_node(AST_Binary, self, {
9472                         operator: "||",
9473                         left: make_node(AST_Sub, self, {
9474                             expression: exp.expression,
9475                             property: self.args.length ? make_node(AST_Binary, self.args[0], {
9476                                 operator: "|",
9477                                 left: make_node(AST_Number, self, { value: 0 }),
9478                                 right: self.args[0],
9479                             }) : make_node(AST_Number, self, { value: 0 }),
9480                         }).optimize(compressor),
9481                         right: make_node(AST_String, self, { value: "" }),
9482                     });
9483                     node.is_string = return_true;
9484                     return node.optimize(compressor);
9485                 }
9486                 break;
9487               case "apply":
9488                 if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
9489                     var args = self.args[1].elements.slice();
9490                     args.unshift(self.args[0]);
9491                     return make_node(AST_Call, self, {
9492                         expression: make_node(AST_Dot, exp, {
9493                             expression: exp.expression,
9494                             property: "call",
9495                         }),
9496                         args: args
9497                     }).optimize(compressor);
9498                 }
9499                 break;
9500               case "call":
9501                 var func = exp.expression;
9502                 if (func instanceof AST_SymbolRef) {
9503                     func = func.fixed_value();
9504                 }
9505                 if (func instanceof AST_Lambda && !func.contains_this()) {
9506                     return (self.args.length ? make_sequence(this, [
9507                         self.args[0],
9508                         make_node(AST_Call, self, {
9509                             expression: exp.expression,
9510                             args: self.args.slice(1)
9511                         })
9512                     ]) : make_node(AST_Call, self, {
9513                         expression: exp.expression,
9514                         args: []
9515                     })).optimize(compressor);
9516                 }
9517                 break;
9518             }
9519         }
9520         if (compressor.option("unsafe_Function")
9521             && is_undeclared_ref(exp)
9522             && exp.name == "Function") {
9523             // new Function() ---> function(){}
9524             if (self.args.length == 0) return make_node(AST_Function, self, {
9525                 argnames: [],
9526                 body: []
9527             }).init_vars(exp.scope);
9528             if (all(self.args, function(x) {
9529                 return x instanceof AST_String;
9530             })) {
9531                 // quite a corner-case, but we can handle it:
9532                 //   https://github.com/mishoo/UglifyJS/issues/203
9533                 // if the code argument is a constant, then we can minify it.
9534                 try {
9535                     var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
9536                         return arg.value;
9537                     }).join() + "){" + self.args[self.args.length - 1].value + "})";
9538                     var ast = parse(code);
9539                     var mangle = { ie: compressor.option("ie") };
9540                     ast.figure_out_scope(mangle);
9541                     var comp = new Compressor(compressor.options);
9542                     ast = ast.transform(comp);
9543                     ast.figure_out_scope(mangle);
9544                     ast.compute_char_frequency(mangle);
9545                     ast.mangle_names(mangle);
9546                     var fun;
9547                     ast.walk(new TreeWalker(function(node) {
9548                         if (fun) return true;
9549                         if (node instanceof AST_Lambda) {
9550                             fun = node;
9551                             return true;
9552                         }
9553                     }));
9554                     var code = OutputStream();
9555                     AST_BlockStatement.prototype._codegen.call(fun, code);
9556                     self.args = [
9557                         make_node(AST_String, self, {
9558                             value: fun.argnames.map(function(arg) {
9559                                 return arg.print_to_string();
9560                             }).join(),
9561                         }),
9562                         make_node(AST_String, self.args[self.args.length - 1], {
9563                             value: code.get().replace(/^\{|\}$/g, "")
9564                         })
9565                     ];
9566                     return self;
9567                 } catch (ex) {
9568                     if (ex instanceof JS_Parse_Error) {
9569                         AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
9570                         AST_Node.warn(ex.toString());
9571                     } else {
9572                         throw ex;
9573                     }
9574                 }
9575             }
9576         }
9577         var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9578         var parent = compressor.parent(), current = compressor.self();
9579         var is_func = fn instanceof AST_Lambda
9580             && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
9581             && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
9582         var stat = is_func && fn.first_statement();
9583         var has_default = 0, has_destructured = false;
9584         var has_spread = !all(self.args, function(arg) {
9585             return !(arg instanceof AST_Spread);
9586         });
9587         var can_drop = is_func && all(fn.argnames, function(argname, index) {
9588             if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
9589             if (argname instanceof AST_DefaultValue) {
9590                 if (!has_default) has_default = 1;
9591                 var arg = has_default == 1 && self.args[index];
9592                 if (arg && !is_undefined(arg)) has_default = 2;
9593                 if (has_arg_refs(argname.value)) return false;
9594                 argname = argname.name;
9595             }
9596             if (argname instanceof AST_Destructured) {
9597                 has_destructured = true;
9598                 if (has_arg_refs(argname)) return false;
9599             }
9600             return true;
9601         }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn.rest));
9602         var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
9603         if (can_inline && stat instanceof AST_Return) {
9604             var value = stat.value;
9605             if (exp === fn
9606                 && !fn.name
9607                 && (!value || value.is_constant_expression())
9608                 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9609                 return make_sequence(self, convert_args(value)).optimize(compressor);
9610             }
9611         }
9612         if (is_func) {
9613             var def, value, var_assigned = false;
9614             if (can_inline
9615                 && !fn.uses_arguments
9616                 && !fn.pinned()
9617                 && !(fn.name && fn instanceof AST_LambdaExpression)
9618                 && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
9619                     && fn.is_constant_expression(find_scope(compressor)))
9620                 && !has_spread
9621                 && (value = can_flatten_body(stat))
9622                 && !fn.contains_this()) {
9623                 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
9624                 if (can_substitute_directly()) {
9625                     var args = self.args.slice();
9626                     var refs = [];
9627                     var retValue = value.clone(true).transform(new TreeTransformer(function(node) {
9628                         if (node instanceof AST_SymbolRef) {
9629                             var def = node.definition();
9630                             if (fn.variables.get(node.name) !== def) {
9631                                 refs.push(node);
9632                                 return node;
9633                             }
9634                             var index = resolve_index(def);
9635                             var arg = args[index];
9636                             if (!arg) return make_node(AST_Undefined, self);
9637                             args[index] = null;
9638                             var parent = this.parent();
9639                             return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
9640                         }
9641                     }));
9642                     var save_inlined = fn.inlined;
9643                     if (exp !== fn) fn.inlined = true;
9644                     var exprs = [];
9645                     args.forEach(function(arg) {
9646                         if (!arg) return;
9647                         arg = arg.clone(true);
9648                         arg.walk(new TreeWalker(function(node) {
9649                             if (node instanceof AST_SymbolRef) refs.push(node);
9650                         }));
9651                         exprs.push(arg);
9652                     }, []);
9653                     exprs.push(retValue);
9654                     var node = make_sequence(self, exprs).optimize(compressor);
9655                     fn.inlined = save_inlined;
9656                     node = maintain_this_binding(compressor, parent, current, node);
9657                     if (replacing || best_of_expression(node, self) === node) {
9658                         refs.forEach(function(ref) {
9659                             ref.scope = exp === fn ? fn.parent_scope : exp.scope;
9660                             ref.reference();
9661                             var def = ref.definition();
9662                             if (replacing) def.replaced++;
9663                             def.single_use = false;
9664                         });
9665                         return node;
9666                     } else if (!node.has_side_effects(compressor)) {
9667                         self.drop_side_effect_free = function(compressor, first_in_statement) {
9668                             var self = this;
9669                             var exprs = self.args.slice();
9670                             exprs.unshift(self.expression);
9671                             return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement);
9672                         };
9673                     }
9674                 }
9675                 var arg_used, insert, in_loop, scope;
9676                 if (replacing && can_inject_symbols()) {
9677                     fn._squeezed = true;
9678                     if (exp !== fn) fn.parent_scope = exp.scope;
9679                     var node = make_sequence(self, flatten_fn()).optimize(compressor);
9680                     return maintain_this_binding(compressor, parent, current, node);
9681                 }
9682             }
9683             if (compressor.option("side_effects")
9684                 && can_drop
9685                 && all(fn.body, is_empty)
9686                 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
9687                 && !(is_arrow(fn) && fn.value)
9688                 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9689                 return make_sequence(self, convert_args()).optimize(compressor);
9690             }
9691         }
9692         if (compressor.option("drop_console")) {
9693             if (exp instanceof AST_PropAccess) {
9694                 var name = exp.expression;
9695                 while (name.expression) {
9696                     name = name.expression;
9697                 }
9698                 if (is_undeclared_ref(name) && name.name == "console") {
9699                     return make_node(AST_Undefined, self).optimize(compressor);
9700                 }
9701             }
9702         }
9703         if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
9704             return self.negate(compressor, true);
9705         }
9706         return try_evaluate(compressor, self);
9707
9708         function has_arg_refs(node) {
9709             var found = false;
9710             node.walk(new TreeWalker(function(node) {
9711                 if (found) return true;
9712                 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
9713                     return found = true;
9714                 }
9715             }));
9716             return found;
9717         }
9718
9719         function make_void_lhs(orig) {
9720             return make_node(AST_Dot, orig, {
9721                 expression: make_node(AST_Array, orig, { elements: [] }),
9722                 property: "e",
9723             });
9724         }
9725
9726         function convert_args(value) {
9727             var args = self.args.slice();
9728             var destructured = has_default > 1 || has_destructured || fn.rest;
9729             if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
9730             if (destructured) {
9731                 var tt = new TreeTransformer(function(node, descend) {
9732                     if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
9733                         name: node.name.transform(tt) || make_void_lhs(node),
9734                         value: node.value,
9735                     });
9736                     if (node instanceof AST_DestructuredArray) {
9737                         var elements = [];
9738                         node.elements.forEach(function(node, index) {
9739                             node = node.transform(tt);
9740                             if (node) elements[index] = node;
9741                         });
9742                         fill_holes(node, elements);
9743                         return make_node(AST_DestructuredArray, node, { elements: elements });
9744                     }
9745                     if (node instanceof AST_DestructuredObject) {
9746                         var properties = [], side_effects = [];
9747                         node.properties.forEach(function(prop) {
9748                             var key = prop.key;
9749                             var value = prop.value.transform(tt);
9750                             if (value) {
9751                                 if (side_effects.length) {
9752                                     if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop);
9753                                     side_effects.push(key);
9754                                     key = make_sequence(node, side_effects);
9755                                     side_effects = [];
9756                                 }
9757                                 properties.push(make_node(AST_DestructuredKeyVal, prop, {
9758                                     key: key,
9759                                     value: value,
9760                                 }));
9761                             } else if (key instanceof AST_Node) {
9762                                 side_effects.push(key);
9763                             }
9764                         });
9765                         if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
9766                             key: make_sequence(node, side_effects),
9767                             value: make_void_lhs(node),
9768                         }));
9769                         return make_node(AST_DestructuredObject, node, { properties: properties });
9770                     }
9771                     if (node instanceof AST_SymbolFunarg) return null;
9772                 });
9773                 var lhs = [];
9774                 fn.argnames.forEach(function(argname, index) {
9775                     argname = argname.transform(tt);
9776                     if (argname) lhs[index] = argname;
9777                 });
9778                 var rest = fn.rest && fn.rest.transform(tt);
9779                 if (rest) lhs.length = fn.argnames.length;
9780                 fill_holes(fn, lhs);
9781                 args[0] = make_node(AST_Assign, self, {
9782                     operator: "=",
9783                     left: make_node(AST_DestructuredArray, fn, {
9784                         elements: lhs,
9785                         rest: rest,
9786                     }),
9787                     right: args[0],
9788                 });
9789             } else fn.argnames.forEach(function(argname) {
9790                 if (argname instanceof AST_DefaultValue) args.push(argname.value);
9791             });
9792             args.push(value || make_node(AST_Undefined, self));
9793             return args;
9794         }
9795
9796         function safe_from_await_yield(node, scope) {
9797             var avoid = avoid_await_yield(scope);
9798             if (!avoid) return true;
9799             var safe = true;
9800             var tw = new TreeWalker(function(node) {
9801                 if (!safe) return true;
9802                 if (node instanceof AST_Scope) {
9803                     if (node === fn) return;
9804                     if (is_arrow(node)) {
9805                         for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
9806                     } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
9807                         safe = false;
9808                     }
9809                     return true;
9810                 }
9811                 if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
9812             });
9813             node.walk(tw);
9814             return safe;
9815         }
9816
9817         function noop_value() {
9818             return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
9819         }
9820
9821         function return_value(stat) {
9822             if (!stat) return noop_value();
9823             if (stat instanceof AST_Return) return stat.value || noop_value();
9824             if (stat instanceof AST_SimpleStatement) {
9825                 return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, {
9826                     operator: "void",
9827                     expression: stat.body,
9828                 });
9829             }
9830         }
9831
9832         function can_flatten_body(stat) {
9833             var len = fn.body.length;
9834             if (len < 2) {
9835                 stat = return_value(stat);
9836                 if (stat) return stat;
9837             }
9838             if (compressor.option("inline") < 3) return false;
9839             stat = null;
9840             for (var i = 0; i < len; i++) {
9841                 var line = fn.body[i];
9842                 if (line instanceof AST_Var) {
9843                     var assigned = var_assigned || !declarations_only(line);
9844                     if (assigned) {
9845                         var_assigned = true;
9846                         if (stat) return false;
9847                     }
9848                 } else if (line instanceof AST_AsyncDefun
9849                     || line instanceof AST_Defun
9850                     || line instanceof AST_EmptyStatement) {
9851                     continue;
9852                 } else if (stat) {
9853                     return false;
9854                 } else {
9855                     stat = line;
9856                 }
9857             }
9858             return return_value(stat);
9859         }
9860
9861         function resolve_index(def) {
9862             for (var i = fn.argnames.length; --i >= 0;) {
9863                 if (fn.argnames[i].definition() === def) return i;
9864             }
9865         }
9866
9867         function can_substitute_directly() {
9868             if (has_default || has_destructured || var_assigned || fn.rest) return;
9869             if (compressor.option("inline") < 2 && fn.argnames.length) return;
9870             if (!fn.variables.all(function(def) {
9871                 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
9872             })) return;
9873             var scope = compressor.find_parent(AST_Scope);
9874             var abort = false;
9875             var avoid = avoid_await_yield(scope);
9876             var begin;
9877             var in_order = [];
9878             var side_effects = false;
9879             value.walk(new TreeWalker(function(node, descend) {
9880                 if (abort) return true;
9881                 if (node instanceof AST_Binary && lazy_op[node.operator]
9882                     || node instanceof AST_Conditional) {
9883                     in_order = null;
9884                     return;
9885                 }
9886                 if (node instanceof AST_Scope) return abort = true;
9887                 if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
9888                 if (node instanceof AST_SymbolRef) {
9889                     var def = node.definition();
9890                     if (fn.variables.get(node.name) !== def) {
9891                         in_order = null;
9892                         return;
9893                     }
9894                     if (def.init instanceof AST_LambdaDefinition) return abort = true;
9895                     if (is_lhs(node, this.parent())) return abort = true;
9896                     var index = resolve_index(def);
9897                     if (!(begin < index)) begin = index;
9898                     if (!in_order) return;
9899                     if (side_effects) {
9900                         in_order = null;
9901                     } else {
9902                         in_order.push(fn.argnames[index]);
9903                     }
9904                     return;
9905                 }
9906                 if (node.has_side_effects(compressor)) {
9907                     descend();
9908                     side_effects = true;
9909                     return true;
9910                 }
9911             }));
9912             if (abort) return;
9913             var end = self.args.length;
9914             if (in_order && fn.argnames.length >= end) {
9915                 end = fn.argnames.length;
9916                 while (end-- > begin && fn.argnames[end] === in_order.pop());
9917                 end++;
9918             }
9919             return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) {
9920                 return funarg.is_constant_expression(scope);
9921             } : function(funarg) {
9922                 return !funarg.has_side_effects(compressor);
9923             });
9924         }
9925
9926         function var_exists(defined, name) {
9927             return defined[name] || identifier_atom[name] || scope.var_names()[name];
9928         }
9929
9930         function can_inject_args(defined, used, safe_to_inject) {
9931             var abort = false;
9932             fn.each_argname(function(arg) {
9933                 if (abort) return;
9934                 if (arg.__unused) return;
9935                 if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
9936                 used[arg.name] = true;
9937                 if (in_loop) in_loop.push(arg.definition());
9938             });
9939             return !abort;
9940         }
9941
9942         function can_inject_vars(defined, used, safe_to_inject) {
9943             for (var i = 0; i < fn.body.length; i++) {
9944                 var stat = fn.body[i];
9945                 if (stat instanceof AST_LambdaDefinition) {
9946                     if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
9947                     if (!all(stat.enclosed, function(def) {
9948                         return def.scope === stat || !defined[def.name];
9949                     })) return false;
9950                     if (in_loop) in_loop.push(stat.name.definition());
9951                     continue;
9952                 }
9953                 if (!(stat instanceof AST_Var)) continue;
9954                 if (!safe_to_inject) return false;
9955                 for (var j = stat.definitions.length; --j >= 0;) {
9956                     var name = stat.definitions[j].name;
9957                     if (var_exists(defined, name.name)) return false;
9958                     if (in_loop) in_loop.push(name.definition());
9959                 }
9960             }
9961             return true;
9962         }
9963
9964         function can_inject_symbols() {
9965             var defined = Object.create(null);
9966             var level = 0, child;
9967             scope = current;
9968             do {
9969                 if (scope.variables) scope.variables.each(function(def) {
9970                     defined[def.name] = true;
9971                 });
9972                 child = scope;
9973                 scope = compressor.parent(level++);
9974                 if (scope instanceof AST_DWLoop) {
9975                     in_loop = [];
9976                 } else if (scope instanceof AST_For) {
9977                     if (scope.init === child) continue;
9978                     in_loop = [];
9979                 } else if (scope instanceof AST_ForEnumeration) {
9980                     if (scope.init === child) continue;
9981                     if (scope.object === child) continue;
9982                     in_loop = [];
9983                 } else if (scope instanceof AST_SymbolRef) {
9984                     if (scope.fixed_value() instanceof AST_Scope) return false;
9985                 }
9986             } while (!(scope instanceof AST_Scope));
9987             insert = scope.body.indexOf(child) + 1;
9988             if (!insert) return false;
9989             if (!safe_from_await_yield(fn, scope)) return false;
9990             var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
9991             if (scope instanceof AST_Toplevel) {
9992                 if (compressor.toplevel.vars) {
9993                     defined["arguments"] = true;
9994                 } else {
9995                     safe_to_inject = false;
9996                 }
9997             }
9998             var inline = compressor.option("inline");
9999             arg_used = Object.create(defined);
10000             if (!can_inject_args(defined, arg_used, inline >= 2 && safe_to_inject)) return false;
10001             var used = Object.create(arg_used);
10002             if (!can_inject_vars(defined, used, inline >= 3 && safe_to_inject)) return false;
10003             return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
10004         }
10005
10006         function append_var(decls, expressions, name, value) {
10007             var def = name.definition();
10008             if (!scope.var_names()[name.name]) {
10009                 scope.var_names()[name.name] = true;
10010                 decls.push(make_node(AST_VarDef, name, {
10011                     name: name,
10012                     value: null,
10013                 }));
10014             }
10015             scope.variables.set(name.name, def);
10016             scope.enclosed.push(def);
10017             if (!value) return;
10018             var sym = make_node(AST_SymbolRef, name, name);
10019             def.references.push(sym);
10020             expressions.push(make_node(AST_Assign, self, {
10021                 operator: "=",
10022                 left: sym,
10023                 right: value,
10024             }));
10025         }
10026
10027         function flatten_args(decls, expressions) {
10028             var len = fn.argnames.length;
10029             for (var i = self.args.length; --i >= len;) {
10030                 expressions.push(self.args[i]);
10031             }
10032             var default_args = [];
10033             for (i = len; --i >= 0;) {
10034                 var argname = fn.argnames[i];
10035                 var name;
10036                 if (argname instanceof AST_DefaultValue) {
10037                     default_args.push(argname);
10038                     name = argname.name;
10039                 } else {
10040                     name = argname;
10041                 }
10042                 var value = self.args[i];
10043                 if (name.__unused || scope.var_names()[name.name]) {
10044                     if (value) expressions.push(value);
10045                 } else {
10046                     var symbol = make_node(AST_SymbolVar, name, name);
10047                     name.definition().orig.push(symbol);
10048                     if ("__unused" in name) {
10049                         append_var(decls, expressions, symbol);
10050                         if (value) expressions.push(value);
10051                     } else {
10052                         if (!value && in_loop && argname === name) value = make_node(AST_Undefined, self);
10053                         append_var(decls, expressions, symbol, value);
10054                     }
10055                 }
10056             }
10057             decls.reverse();
10058             expressions.reverse();
10059             for (i = default_args.length; --i >= 0;) {
10060                 var node = default_args[i];
10061                 if ("__unused" in node.name) {
10062                     expressions.push(node.value);
10063                 } else {
10064                     var sym = make_node(AST_SymbolRef, node.name, node.name);
10065                     node.name.definition().references.push(sym);
10066                     expressions.push(make_node(AST_Assign, node, {
10067                         operator: "=",
10068                         left: sym,
10069                         right: node.value,
10070                     }));
10071                 }
10072             }
10073         }
10074
10075         function flatten_destructured(decls, expressions) {
10076             expressions.push(make_node(AST_Assign, self, {
10077                 operator: "=",
10078                 left: make_node(AST_DestructuredArray, self, {
10079                     elements: fn.argnames.map(function(argname) {
10080                         if (argname.__unused) return make_node(AST_Hole, argname);
10081                         return argname.convert_symbol(AST_SymbolRef, process);
10082                     }),
10083                     rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
10084                 }),
10085                 right: make_node(AST_Array, self, { elements: self.args.slice() }),
10086             }));
10087
10088             function process(ref, name) {
10089                 var def = name.definition();
10090                 def.references.push(ref);
10091                 var symbol = make_node(AST_SymbolVar, name, name);
10092                 def.orig.push(symbol);
10093                 append_var(decls, expressions, symbol);
10094             }
10095         }
10096
10097         function flatten_var(name) {
10098             var redef = name.definition().redefined();
10099             if (redef) {
10100                 name = name.clone();
10101                 name.thedef = redef;
10102             }
10103             return name;
10104         }
10105
10106         function flatten_vars(decls, expressions) {
10107             var args = [ insert, 0 ];
10108             var decl_var = [], expr_var = [], expr_loop = [];
10109             for (var i = 0; i < fn.body.length; i++) {
10110                 var stat = fn.body[i];
10111                 if (stat instanceof AST_LambdaDefinition) {
10112                     if (in_loop) {
10113                         var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
10114                         name.definition().orig.push(name);
10115                         append_var(decls, expressions, name, to_func_expr(stat, true));
10116                     } else {
10117                         var def = stat.name.definition();
10118                         scope.functions.set(def.name, def);
10119                         scope.variables.set(def.name, def);
10120                         scope.enclosed.push(def);
10121                         scope.var_names()[def.name] = true;
10122                         args.push(stat);
10123                     }
10124                     continue;
10125                 }
10126                 if (!(stat instanceof AST_Var)) continue;
10127                 for (var j = 0; j < stat.definitions.length; j++) {
10128                     var var_def = stat.definitions[j];
10129                     var name = flatten_var(var_def.name);
10130                     append_var(decl_var, expr_var, name, var_def.value);
10131                     if (in_loop && !HOP(arg_used, name.name)) {
10132                         var def = fn.variables.get(name.name);
10133                         var sym = make_node(AST_SymbolRef, name, name);
10134                         def.references.push(sym);
10135                         expr_loop.push(make_node(AST_Assign, var_def, {
10136                             operator: "=",
10137                             left: sym,
10138                             right: make_node(AST_Undefined, name),
10139                         }));
10140                     }
10141                 }
10142             }
10143             [].push.apply(decls, decl_var);
10144             [].push.apply(expressions, expr_loop);
10145             [].push.apply(expressions, expr_var);
10146             return args;
10147         }
10148
10149         function flatten_fn() {
10150             var decls = [];
10151             var expressions = [];
10152             if (has_default > 1 || has_destructured || fn.rest) {
10153                 flatten_destructured(decls, expressions);
10154             } else {
10155                 flatten_args(decls, expressions);
10156             }
10157             var args = flatten_vars(decls, expressions);
10158             expressions.push(value);
10159             if (decls.length) args.push(make_node(AST_Var, fn, {
10160                 definitions: decls
10161             }));
10162             [].splice.apply(scope.body, args);
10163             fn.enclosed.forEach(function(def) {
10164                 if (scope.var_names()[def.name]) return;
10165                 scope.enclosed.push(def);
10166                 scope.var_names()[def.name] = true;
10167             });
10168             return expressions;
10169         }
10170     });
10171
10172     OPT(AST_New, function(self, compressor) {
10173         if (compressor.option("sequences")) {
10174             var seq = lift_sequence_in_expression(self, compressor);
10175             if (seq !== self) return seq.optimize(compressor);
10176         }
10177         if (compressor.option("unused")) drop_unused_call_args(self, compressor);
10178         if (compressor.option("unsafe")) {
10179             var exp = self.expression;
10180             if (is_undeclared_ref(exp)) {
10181                 switch (exp.name) {
10182                   case "Object":
10183                   case "RegExp":
10184                   case "Function":
10185                   case "Error":
10186                   case "Array":
10187                     return make_node(AST_Call, self, self).transform(compressor);
10188                 }
10189             }
10190         }
10191         return self;
10192     });
10193
10194     // (a = b, x && a = c) ---> a = x ? c : b
10195     // (a = b, x || a = c) ---> a = x ? b : c
10196     function to_conditional_assignment(compressor, def, value, node) {
10197         if (!(node instanceof AST_Binary)) return;
10198         if (!(node.operator == "&&" || node.operator == "||")) return;
10199         if (!(node.right instanceof AST_Assign)) return;
10200         if (node.right.operator != "=") return;
10201         if (!(node.right.left instanceof AST_SymbolRef)) return;
10202         if (node.right.left.definition() !== def) return;
10203         if (value.has_side_effects(compressor)) return;
10204         if (!safe_from_assignment(node.left)) return;
10205         if (!safe_from_assignment(node.right.right)) return;
10206         def.replaced++;
10207         return node.operator == "&&" ? make_node(AST_Conditional, node, {
10208             condition: node.left,
10209             consequent: node.right.right,
10210             alternative: value
10211         }) : make_node(AST_Conditional, node, {
10212             condition: node.left,
10213             consequent: value,
10214             alternative: node.right.right
10215         });
10216
10217         function safe_from_assignment(node) {
10218             if (node.has_side_effects(compressor)) return;
10219             var hit = false;
10220             node.walk(new TreeWalker(function(node) {
10221                 if (hit) return true;
10222                 if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
10223             }));
10224             return !hit;
10225         }
10226     }
10227
10228     OPT(AST_Sequence, function(self, compressor) {
10229         var expressions = filter_for_side_effects();
10230         var end = expressions.length - 1;
10231         merge_assignments();
10232         trim_right_for_undefined();
10233         if (end == 0) {
10234             self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
10235             if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
10236             return self;
10237         }
10238         self.expressions = expressions;
10239         return self;
10240
10241         function filter_for_side_effects() {
10242             if (!compressor.option("side_effects")) return self.expressions;
10243             var expressions = [];
10244             var first = first_in_statement(compressor);
10245             var last = self.expressions.length - 1;
10246             self.expressions.forEach(function(expr, index) {
10247                 if (index < last) expr = expr.drop_side_effect_free(compressor, first);
10248                 if (expr) {
10249                     merge_sequence(expressions, expr);
10250                     first = false;
10251                 }
10252             });
10253             return expressions;
10254         }
10255
10256         function trim_right_for_undefined() {
10257             if (!compressor.option("side_effects")) return;
10258             while (end > 0 && is_undefined(expressions[end], compressor)) end--;
10259             if (end < expressions.length - 1) {
10260                 expressions[end] = make_node(AST_UnaryPrefix, self, {
10261                     operator   : "void",
10262                     expression : expressions[end]
10263                 });
10264                 expressions.length = end + 1;
10265             }
10266         }
10267
10268         function is_simple_assign(node) {
10269             return node instanceof AST_Assign
10270                 && node.operator == "="
10271                 && node.left instanceof AST_SymbolRef
10272                 && node.left.definition();
10273         }
10274
10275         function merge_assignments() {
10276             for (var i = 1; i < end; i++) {
10277                 var prev = expressions[i - 1];
10278                 var def = is_simple_assign(prev);
10279                 if (!def) continue;
10280                 var expr = expressions[i];
10281                 if (compressor.option("conditionals")) {
10282                     var cond = to_conditional_assignment(compressor, def, prev.right, expr);
10283                     if (cond) {
10284                         prev.right = cond;
10285                         expressions.splice(i--, 1);
10286                         end--;
10287                         continue;
10288                     }
10289                 }
10290                 if (compressor.option("dead_code")
10291                     && is_simple_assign(expr) === def
10292                     && expr.right.is_constant_expression(def.scope.resolve())) {
10293                     expressions[--i] = prev.right;
10294                 }
10295             }
10296         }
10297     });
10298
10299     OPT(AST_UnaryPostfix, function(self, compressor) {
10300         if (compressor.option("sequences")) {
10301             var seq = lift_sequence_in_expression(self, compressor);
10302             if (seq !== self) return seq.optimize(compressor);
10303         }
10304         return try_evaluate(compressor, self);
10305     });
10306
10307     var SIGN_OPS = makePredicate("+ -");
10308     var MULTIPLICATIVE_OPS = makePredicate("* / %");
10309     OPT(AST_UnaryPrefix, function(self, compressor) {
10310         var op = self.operator;
10311         var exp = self.expression;
10312         if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
10313             return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
10314         }
10315         if (compressor.option("sequences") && can_lift()) {
10316             var seq = lift_sequence_in_expression(self, compressor);
10317             if (seq !== self) return seq.optimize(compressor);
10318         }
10319         if (compressor.option("side_effects") && op == "void") {
10320             exp = exp.drop_side_effect_free(compressor);
10321             if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
10322             self.expression = exp;
10323             return self;
10324         }
10325         if (compressor.option("booleans")) {
10326             if (op == "!" && exp.is_truthy()) {
10327                 return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
10328             } else if (compressor.in_boolean_context()) switch (op) {
10329               case "!":
10330                 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
10331                     // !!foo ---> foo, if we're in boolean context
10332                     return exp.expression;
10333                 }
10334                 if (exp instanceof AST_Binary) {
10335                     self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
10336                 }
10337                 break;
10338               case "typeof":
10339                 // typeof always returns a non-empty string, thus it's
10340                 // always true in booleans
10341                 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
10342                 var exprs = [ make_node(AST_True, self) ];
10343                 if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
10344                 return make_sequence(self, exprs).optimize(compressor);
10345             }
10346         }
10347         if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
10348         if (compressor.option("evaluate")
10349             && exp instanceof AST_Binary
10350             && SIGN_OPS[op]
10351             && MULTIPLICATIVE_OPS[exp.operator]
10352             && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
10353             return make_node(AST_Binary, self, {
10354                 operator: exp.operator,
10355                 left: make_node(AST_UnaryPrefix, exp.left, {
10356                     operator: op,
10357                     expression: exp.left
10358                 }),
10359                 right: exp.right
10360             });
10361         }
10362         // avoids infinite recursion of numerals
10363         return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
10364             ? self : try_evaluate(compressor, self);
10365
10366         function may_not_delete(node) {
10367             return node instanceof AST_Infinity
10368                 || node instanceof AST_NaN
10369                 || node instanceof AST_NewTarget
10370                 || node instanceof AST_PropAccess
10371                 || node instanceof AST_SymbolRef
10372                 || node instanceof AST_Undefined;
10373         }
10374
10375         function can_lift() {
10376             switch (op) {
10377               case "delete":
10378                 return !may_not_delete(exp.tail_node());
10379               case "typeof":
10380                 return !is_undeclared_ref(exp.tail_node());
10381               default:
10382                 return true;
10383             }
10384         }
10385     });
10386
10387     OPT(AST_Await, function(self, compressor) {
10388         if (!compressor.option("awaits")) return self;
10389         if (compressor.option("sequences")) {
10390             var seq = lift_sequence_in_expression(self, compressor);
10391             if (seq !== self) return seq.optimize(compressor);
10392         }
10393         if (compressor.option("side_effects")) {
10394             var exp = self.expression;
10395             if (exp instanceof AST_Await) return exp.optimize(compressor);
10396             if (exp instanceof AST_UnaryPrefix) {
10397                 if (exp.expression instanceof AST_Await) return exp.optimize(compressor);
10398                 if (exp.operator == "void") return make_node(AST_UnaryPrefix, self, {
10399                     operator: "void",
10400                     expression: make_node(AST_Await, self, { expression: exp.expression }),
10401                 }).optimize(compressor);
10402             }
10403             for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) {
10404                 if (is_arrow(parent)) {
10405                     if (parent.value === node) return exp.optimize(compressor);
10406                 } else if (parent instanceof AST_Return) {
10407                     var drop = true;
10408                     do {
10409                         node = parent;
10410                         parent = compressor.parent(level++);
10411                         if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) {
10412                             drop = false;
10413                             break;
10414                         }
10415                     } while (parent && !(parent instanceof AST_Scope));
10416                     if (drop) return exp.optimize(compressor);
10417                 } else if (parent instanceof AST_Sequence) {
10418                     if (parent.tail_node() === node) continue;
10419                 }
10420                 break;
10421             }
10422         }
10423         return self;
10424     });
10425
10426     OPT(AST_Yield, function(self, compressor) {
10427         if (!compressor.option("yields")) return self;
10428         if (compressor.option("sequences")) {
10429             var seq = lift_sequence_in_expression(self, compressor);
10430             if (seq !== self) return seq.optimize(compressor);
10431         }
10432         var exp = self.expression;
10433         if (self.nested && exp.TYPE == "Call") {
10434             var inlined = exp.clone().optimize(compressor);
10435             if (inlined.TYPE != "Call") return inlined;
10436         }
10437         return self;
10438     });
10439
10440     AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
10441         if (this.left instanceof AST_PropAccess) {
10442             if (!(this.left.expression instanceof AST_Sequence)) return this;
10443             var x = this.left.expression.expressions.slice();
10444             var e = this.clone();
10445             e.left = e.left.clone();
10446             e.left.expression = x.pop();
10447             x.push(e);
10448             return make_sequence(this, x);
10449         }
10450         if (this.left instanceof AST_Sequence) {
10451             var x = this.left.expressions.slice();
10452             var e = this.clone();
10453             e.left = x.pop();
10454             x.push(e);
10455             return make_sequence(this, x);
10456         }
10457         if (this.right instanceof AST_Sequence) {
10458             if (this.left.has_side_effects(compressor)) return this;
10459             var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
10460             var x = this.right.expressions;
10461             var last = x.length - 1;
10462             for (var i = 0; i < last; i++) {
10463                 if (!assign && x[i].has_side_effects(compressor)) break;
10464             }
10465             if (i == last) {
10466                 x = x.slice();
10467                 var e = this.clone();
10468                 e.right = x.pop();
10469                 x.push(e);
10470                 return make_sequence(this, x);
10471             }
10472             if (i > 0) {
10473                 var e = this.clone();
10474                 e.right = make_sequence(this.right, x.slice(i));
10475                 x = x.slice(0, i);
10476                 x.push(e);
10477                 return make_sequence(this, x);
10478             }
10479         }
10480         return this;
10481     });
10482
10483     var indexFns = makePredicate("indexOf lastIndexOf");
10484     var commutativeOperators = makePredicate("== === != !== * & | ^");
10485     function is_object(node) {
10486         if (node instanceof AST_Assign) return node.operator == "=" && is_object(node.right);
10487         if (node instanceof AST_Sequence) return is_object(node.tail_node());
10488         if (node instanceof AST_SymbolRef) return is_object(node.fixed_value());
10489         return node instanceof AST_Array
10490             || node instanceof AST_Class
10491             || node instanceof AST_Lambda
10492             || node instanceof AST_New
10493             || node instanceof AST_Object;
10494     }
10495
10496     function is_primitive(compressor, node) {
10497         if (node.is_constant()) return true;
10498         if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right);
10499         if (node instanceof AST_Binary) {
10500             return !lazy_op[node.operator]
10501                 || is_primitive(compressor, node.left) && is_primitive(compressor, node.right);
10502         }
10503         if (node instanceof AST_Conditional) {
10504             return is_primitive(compressor, node.consequent) && is_primitive(compressor, node.alternative);
10505         }
10506         if (node instanceof AST_Sequence) return is_primitive(compressor, node.tail_node());
10507         if (node instanceof AST_SymbolRef) {
10508             var fixed = node.fixed_value();
10509             return fixed && is_primitive(compressor, fixed);
10510         }
10511         if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag);
10512         if (node instanceof AST_Unary) return true;
10513     }
10514
10515     function repeatable(compressor, node) {
10516         if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
10517         if (node instanceof AST_Sub) {
10518             return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
10519         }
10520         if (node instanceof AST_Symbol) return true;
10521         return !node.has_side_effects(compressor);
10522     }
10523
10524     OPT(AST_Binary, function(self, compressor) {
10525         function reversible() {
10526             return self.left.is_constant()
10527                 || self.right.is_constant()
10528                 || !self.left.has_side_effects(compressor)
10529                     && !self.right.has_side_effects(compressor);
10530         }
10531         function reverse(op) {
10532             if (reversible()) {
10533                 if (op) self.operator = op;
10534                 var tmp = self.left;
10535                 self.left = self.right;
10536                 self.right = tmp;
10537             }
10538         }
10539         function swap_chain() {
10540             var rhs = self.right;
10541             self.left = make_node(AST_Binary, self, {
10542                 operator: self.operator,
10543                 left: self.left,
10544                 right: rhs.left,
10545                 start: self.left.start,
10546                 end: rhs.left.end
10547             });
10548             self.right = rhs.right;
10549             self.left = self.left.transform(compressor);
10550         }
10551         if (commutativeOperators[self.operator]
10552             && self.right.is_constant()
10553             && !self.left.is_constant()
10554             && !(self.left instanceof AST_Binary
10555                 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10556             // if right is a constant, whatever side effects the
10557             // left side might have could not influence the
10558             // result.  hence, force switch.
10559             reverse();
10560         }
10561         if (compressor.option("sequences")) {
10562             var seq = self.lift_sequences(compressor);
10563             if (seq !== self) return seq.optimize(compressor);
10564         }
10565         if (compressor.option("assignments") && lazy_op[self.operator]) {
10566             var assign = self.right;
10567             // a || (a = x) ---> a = a || x
10568             // a && (a = x) ---> a = a && x
10569             if (self.left instanceof AST_SymbolRef
10570                 && assign instanceof AST_Assign
10571                 && assign.operator == "="
10572                 && self.left.equivalent_to(assign.left)) {
10573                 self.right = assign.right;
10574                 assign.right = self;
10575                 return assign;
10576             }
10577         }
10578         if (compressor.option("comparisons")) switch (self.operator) {
10579           case "===":
10580           case "!==":
10581             if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
10582                 AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
10583                 return make_sequence(self, [
10584                     self.right,
10585                     make_node(self.operator == "===" ? AST_False : AST_True, self)
10586                 ]).optimize(compressor);
10587             }
10588             var is_strict_comparison = true;
10589             if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
10590                 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
10591                 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
10592                 repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
10593                 self.operator = self.operator.slice(0, 2);
10594             }
10595             // XXX: intentionally falling down to the next case
10596           case "==":
10597           case "!=":
10598             // void 0 == x ---> null == x
10599             if (!is_strict_comparison && is_undefined(self.left, compressor)) {
10600                 self.left = make_node(AST_Null, self.left);
10601             }
10602             // "undefined" == typeof x ---> undefined === x
10603             else if (compressor.option("typeofs")
10604                 && self.left instanceof AST_String
10605                 && self.left.value == "undefined"
10606                 && self.right instanceof AST_UnaryPrefix
10607                 && self.right.operator == "typeof") {
10608                 var expr = self.right.expression;
10609                 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
10610                     : !(expr instanceof AST_PropAccess && compressor.option("ie"))) {
10611                     self.right = expr;
10612                     self.left = make_node(AST_Undefined, self.left).optimize(compressor);
10613                     if (self.operator.length == 2) self.operator += "=";
10614                 }
10615             }
10616             // obj !== obj ---> false
10617             else if (self.left instanceof AST_SymbolRef
10618                 && self.right instanceof AST_SymbolRef
10619                 && self.left.definition() === self.right.definition()
10620                 && is_object(self.left)) {
10621                 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
10622             }
10623             break;
10624           case "&&":
10625           case "||":
10626             // void 0 !== x && null !== x ---> null != x
10627             // void 0 === x || null === x ---> null == x
10628             var lhs = self.left;
10629             if (lhs.operator == self.operator) {
10630                 lhs = lhs.right;
10631             }
10632             if (lhs instanceof AST_Binary
10633                 && lhs.operator == (self.operator == "&&" ? "!==" : "===")
10634                 && self.right instanceof AST_Binary
10635                 && lhs.operator == self.right.operator
10636                 && (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
10637                     || lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
10638                 && !lhs.right.has_side_effects(compressor)
10639                 && lhs.right.equivalent_to(self.right.right)) {
10640                 var combined = make_node(AST_Binary, self, {
10641                     operator: lhs.operator.slice(0, -1),
10642                     left: make_node(AST_Null, self),
10643                     right: lhs.right
10644                 });
10645                 if (lhs !== self.left) {
10646                     combined = make_node(AST_Binary, self, {
10647                         operator: self.operator,
10648                         left: self.left.left,
10649                         right: combined
10650                     });
10651                 }
10652                 return combined;
10653             }
10654             break;
10655         }
10656         var in_bool = false;
10657         var parent = compressor.parent();
10658         if (compressor.option("booleans")) {
10659             var lhs = self.left;
10660             if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
10661                 if (lhs.equivalent_to(self.right)) {
10662                     return maintain_this_binding(compressor, parent, compressor.self(), lhs).optimize(compressor);
10663                 }
10664                 mark_duplicate_condition(compressor, lhs);
10665             }
10666             in_bool = compressor.in_boolean_context();
10667         }
10668         if (in_bool) switch (self.operator) {
10669           case "+":
10670             var ll = self.left.evaluate(compressor);
10671             var rr = self.right.evaluate(compressor);
10672             if (ll && typeof ll == "string") {
10673                 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10674                 return make_sequence(self, [
10675                     self.right,
10676                     make_node(AST_True, self)
10677                 ]).optimize(compressor);
10678             }
10679             if (rr && typeof rr == "string") {
10680                 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10681                 return make_sequence(self, [
10682                     self.left,
10683                     make_node(AST_True, self)
10684                 ]).optimize(compressor);
10685             }
10686             break;
10687           case "==":
10688             if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10689                 return make_node(AST_UnaryPrefix, self, {
10690                     operator: "!",
10691                     expression: self.right
10692                 }).optimize(compressor);
10693             }
10694             break;
10695           case "!=":
10696             if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10697                 return self.right.optimize(compressor);
10698             }
10699             break;
10700         }
10701         if (compressor.option("comparisons") && self.is_boolean(compressor)) {
10702             if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
10703                 var negated = best_of(compressor, self, make_node(AST_UnaryPrefix, self, {
10704                     operator: "!",
10705                     expression: self.negate(compressor, first_in_statement(compressor))
10706                 }));
10707                 if (negated !== self) return negated;
10708             }
10709             switch (self.operator) {
10710               case ">": reverse("<"); break;
10711               case ">=": reverse("<="); break;
10712             }
10713         }
10714         // x && (y && z) ---> x && y && z
10715         // x || (y || z) ---> x || y || z
10716         if (compressor.option("conditionals")
10717             && lazy_op[self.operator]
10718             && self.right instanceof AST_Binary
10719             && self.operator == self.right.operator) {
10720             swap_chain();
10721         }
10722         if (compressor.option("strings") && self.operator == "+") {
10723             // "foo" + 42 + "" ---> "foo" + 42
10724             if (self.right instanceof AST_String
10725                 && self.right.value == ""
10726                 && self.left.is_string(compressor)) {
10727                 return self.left.optimize(compressor);
10728             }
10729             // "" + ("foo" + 42) ---> "foo" + 42
10730             if (self.left instanceof AST_String
10731                 && self.left.value == ""
10732                 && self.right.is_string(compressor)) {
10733                 return self.right.optimize(compressor);
10734             }
10735             // "" + 42 + "foo" ---> 42 + "foo"
10736             if (self.left instanceof AST_Binary
10737                 && self.left.operator == "+"
10738                 && self.left.left instanceof AST_String
10739                 && self.left.left.value == ""
10740                 && self.right.is_string(compressor)
10741                 && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) {
10742                 self.left = self.left.right;
10743                 return self.optimize(compressor);
10744             }
10745             // "x" + (y + "z") ---> "x" + y + "z"
10746             // x + ("y" + z) ---> x + "y" + z
10747             if (self.right instanceof AST_Binary
10748                 && self.operator == self.right.operator
10749                 && (self.left.is_string(compressor) && self.right.is_string(compressor)
10750                     || self.right.left.is_string(compressor)
10751                         && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
10752                 swap_chain();
10753             }
10754         }
10755         if (compressor.option("evaluate")) {
10756             var associative = true;
10757             switch (self.operator) {
10758               case "&&":
10759                 var ll = fuzzy_eval(compressor, self.left);
10760                 if (!ll) {
10761                     AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
10762                     return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10763                 } else if (!(ll instanceof AST_Node)) {
10764                     AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
10765                     return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10766                 }
10767                 var rr = self.right.evaluate(compressor);
10768                 if (!rr) {
10769                     if (in_bool) {
10770                         AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
10771                         return make_sequence(self, [
10772                             self.left,
10773                             make_node(AST_False, self)
10774                         ]).optimize(compressor);
10775                     } else self.falsy = true;
10776                 } else if (!(rr instanceof AST_Node)) {
10777                     if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
10778                         AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
10779                         return self.left.optimize(compressor);
10780                     }
10781                 }
10782                 // (x || false) && y ---> x ? y : false
10783                 if (self.left.operator == "||") {
10784                     var lr = fuzzy_eval(compressor, self.left.right);
10785                     if (!lr) return make_node(AST_Conditional, self, {
10786                         condition: self.left.left,
10787                         consequent: self.right,
10788                         alternative: self.left.right
10789                     }).optimize(compressor);
10790                 }
10791                 break;
10792               case "??":
10793                 var nullish = true;
10794               case "||":
10795                 var ll = fuzzy_eval(compressor, self.left, nullish);
10796                 if (nullish ? ll == null : !ll) {
10797                     AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10798                         operator: self.operator,
10799                         value: nullish ? "nulish" : "false",
10800                         file: self.start.file,
10801                         line: self.start.line,
10802                         col: self.start.col,
10803                     });
10804                     return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10805                 } else if (!(ll instanceof AST_Node)) {
10806                     AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10807                         operator: self.operator,
10808                         value: nullish ? "defined" : "true",
10809                         file: self.start.file,
10810                         line: self.start.line,
10811                         col: self.start.col,
10812                     });
10813                     return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10814                 }
10815                 var rr = self.right.evaluate(compressor);
10816                 if (!rr) {
10817                     if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
10818                         AST_Node.warn("Dropping side-effect-free {operator} [{file}:{line},{col}]", {
10819                             operator: self.operator,
10820                             file: self.start.file,
10821                             line: self.start.line,
10822                             col: self.start.col,
10823                         });
10824                         return self.left.optimize(compressor);
10825                     }
10826                 } else if (!nullish && !(rr instanceof AST_Node)) {
10827                     if (in_bool) {
10828                         AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
10829                         return make_sequence(self, [
10830                             self.left,
10831                             make_node(AST_True, self)
10832                         ]).optimize(compressor);
10833                     } else self.truthy = true;
10834                 }
10835                 // x && true || y ---> x ? true : y
10836                 if (!nullish && self.left.operator == "&&") {
10837                     var lr = fuzzy_eval(compressor, self.left.right);
10838                     if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
10839                         condition: self.left.left,
10840                         consequent: self.left.right,
10841                         alternative: self.right
10842                     }).optimize(compressor);
10843                 }
10844                 break;
10845               case "+":
10846                 // "foo" + ("bar" + x) ---> "foobar" + x
10847                 if (self.left instanceof AST_Constant
10848                     && self.right instanceof AST_Binary
10849                     && self.right.operator == "+"
10850                     && self.right.left instanceof AST_Constant
10851                     && self.right.is_string(compressor)) {
10852                     self = make_node(AST_Binary, self, {
10853                         operator: "+",
10854                         left: make_node(AST_String, self.left, {
10855                             value: "" + self.left.value + self.right.left.value,
10856                             start: self.left.start,
10857                             end: self.right.left.end
10858                         }),
10859                         right: self.right.right
10860                     });
10861                 }
10862                 // (x + "foo") + "bar" ---> x + "foobar"
10863                 if (self.right instanceof AST_Constant
10864                     && self.left instanceof AST_Binary
10865                     && self.left.operator == "+"
10866                     && self.left.right instanceof AST_Constant
10867                     && self.left.is_string(compressor)) {
10868                     self = make_node(AST_Binary, self, {
10869                         operator: "+",
10870                         left: self.left.left,
10871                         right: make_node(AST_String, self.right, {
10872                             value: "" + self.left.right.value + self.right.value,
10873                             start: self.left.right.start,
10874                             end: self.right.end
10875                         })
10876                     });
10877                 }
10878                 // a + -b ---> a - b
10879                 if (self.right instanceof AST_UnaryPrefix
10880                     && self.right.operator == "-"
10881                     && self.left.is_number(compressor)) {
10882                     self = make_node(AST_Binary, self, {
10883                         operator: "-",
10884                         left: self.left,
10885                         right: self.right.expression
10886                     });
10887                     break;
10888                 }
10889                 // -a + b ---> b - a
10890                 if (self.left instanceof AST_UnaryPrefix
10891                     && self.left.operator == "-"
10892                     && reversible()
10893                     && self.right.is_number(compressor)) {
10894                     self = make_node(AST_Binary, self, {
10895                         operator: "-",
10896                         left: self.right,
10897                         right: self.left.expression
10898                     });
10899                     break;
10900                 }
10901                 // (a + b) + 3 ---> 3 + (a + b)
10902                 if (compressor.option("unsafe_math")
10903                     && self.left instanceof AST_Binary
10904                     && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10905                     && self.right.is_constant()
10906                     && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10907                     && self.left.is_number(compressor)
10908                     && !self.left.right.is_constant()
10909                     && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
10910                     self = make_node(AST_Binary, self, {
10911                         operator: self.left.operator,
10912                         left: make_node(AST_Binary, self, {
10913                             operator: self.operator,
10914                             left: self.right,
10915                             right: self.left.left
10916                         }),
10917                         right: self.left.right
10918                     });
10919                     break;
10920                 }
10921               case "-":
10922                 // a - -b ---> a + b
10923                 if (self.right instanceof AST_UnaryPrefix
10924                     && self.right.operator == "-"
10925                     && self.left.is_number(compressor)
10926                     && self.right.expression.is_number(compressor)) {
10927                     self = make_node(AST_Binary, self, {
10928                         operator: "+",
10929                         left: self.left,
10930                         right: self.right.expression
10931                     });
10932                     break;
10933                 }
10934               case "*":
10935               case "/":
10936                 associative = compressor.option("unsafe_math");
10937                 // +a - b ---> a - b
10938                 // a - +b ---> a - b
10939                 if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
10940                     var node = self[operand];
10941                     if (node instanceof AST_UnaryPrefix && node.operator == "+") {
10942                         var exp = node.expression;
10943                         if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) {
10944                             self[operand] = exp;
10945                         }
10946                     }
10947                 });
10948               case "&":
10949               case "|":
10950               case "^":
10951                 // a + +b ---> +b + a
10952                 if (self.operator != "-"
10953                     && self.operator != "/"
10954                     && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
10955                     && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10956                     && reversible()
10957                     && !(self.left instanceof AST_Binary
10958                         && self.left.operator != self.operator
10959                         && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10960                     var reversed = make_node(AST_Binary, self, {
10961                         operator: self.operator,
10962                         left: self.right,
10963                         right: self.left
10964                     });
10965                     if (self.right instanceof AST_Constant
10966                         && !(self.left instanceof AST_Constant)) {
10967                         self = best_of(compressor, reversed, self);
10968                     } else {
10969                         self = best_of(compressor, self, reversed);
10970                     }
10971                 }
10972                 if (!associative || !self.is_number(compressor)) break;
10973                 // a + (b + c) ---> (a + b) + c
10974                 if (self.right instanceof AST_Binary
10975                     && self.right.operator != "%"
10976                     && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
10977                     && self.right.is_number(compressor)
10978                     && (self.operator != "+"
10979                         || self.right.left.is_boolean(compressor)
10980                         || self.right.left.is_number(compressor))
10981                     && (self.operator != "-" || !self.left.is_negative_zero())
10982                     && (self.right.left.is_constant_expression()
10983                         || !self.right.right.has_side_effects(compressor))
10984                     && !is_modify_array(self.right.right)) {
10985                     self = make_node(AST_Binary, self, {
10986                         operator: align(self.operator, self.right.operator),
10987                         left: make_node(AST_Binary, self.left, {
10988                             operator: self.operator,
10989                             left: self.left,
10990                             right: self.right.left,
10991                             start: self.left.start,
10992                             end: self.right.left.end
10993                         }),
10994                         right: self.right.right
10995                     });
10996                     if (self.operator == "+"
10997                         && !self.right.is_boolean(compressor)
10998                         && !self.right.is_number(compressor)) {
10999                         self.right = make_node(AST_UnaryPrefix, self.right, {
11000                             operator: "+",
11001                             expression: self.right
11002                         });
11003                     }
11004                 }
11005                 // (2 * n) * 3 ---> 6 * n
11006                 // (n + 2) + 3 ---> n + 5
11007                 if (self.right instanceof AST_Constant
11008                     && self.left instanceof AST_Binary
11009                     && self.left.operator != "%"
11010                     && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
11011                     && self.left.is_number(compressor)) {
11012                     if (self.left.left instanceof AST_Constant) {
11013                         var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
11014                         self = make_binary(self, self.left.operator, try_evaluate(compressor, lhs), self.left.right);
11015                     } else if (self.left.right instanceof AST_Constant) {
11016                         var op = align(self.left.operator, self.operator);
11017                         var rhs = try_evaluate(compressor, make_binary(self.left, op, self.left.right, self.right));
11018                         if (rhs.is_constant()
11019                             && !(self.left.operator == "-"
11020                                 && self.right.value != 0
11021                                 && +rhs.value == 0
11022                                 && self.left.left.is_negative_zero())) {
11023                             self = make_binary(self, self.left.operator, self.left.left, rhs);
11024                         }
11025                     }
11026                 }
11027                 break;
11028             }
11029             if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11030                 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
11031                   // 0 + n ---> n
11032                   case "+":
11033                     if (self.left.value == 0) {
11034                         if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11035                             operator: "+",
11036                             expression: self.right
11037                         }).optimize(compressor);
11038                         if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
11039                     }
11040                     break;
11041                   // 1 * n ---> n
11042                   case "*":
11043                     if (self.left.value == 1) {
11044                         return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
11045                             operator: "+",
11046                             expression: self.right
11047                         }).optimize(compressor);
11048                     }
11049                     break;
11050                 }
11051                 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
11052                   // n + 0 ---> n
11053                   case "+":
11054                     if (self.right.value == 0) {
11055                         if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11056                             operator: "+",
11057                             expression: self.left
11058                         }).optimize(compressor);
11059                         if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
11060                     }
11061                     break;
11062                   // n - 0 ---> n
11063                   case "-":
11064                     if (self.right.value == 0) {
11065                         return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11066                             operator: "+",
11067                             expression: self.left
11068                         }).optimize(compressor);
11069                     }
11070                     break;
11071                   // n / 1 ---> n
11072                   case "/":
11073                     if (self.right.value == 1) {
11074                         return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11075                             operator: "+",
11076                             expression: self.left
11077                         }).optimize(compressor);
11078                     }
11079                     break;
11080                 }
11081             }
11082         }
11083         if (compressor.option("typeofs")) switch (self.operator) {
11084           case "&&":
11085             mark_locally_defined(self.left, self.right, null);
11086             break;
11087           case "||":
11088             mark_locally_defined(self.left, null, self.right);
11089             break;
11090         }
11091         if (compressor.option("unsafe")) {
11092             var indexRight = is_indexFn(self.right);
11093             if (in_bool
11094                 && indexRight
11095                 && (self.operator == "==" || self.operator == "!=")
11096                 && self.left instanceof AST_Number
11097                 && self.left.value == 0) {
11098                 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
11099                     operator: "!",
11100                     expression: self.right
11101                 }) : self.right).optimize(compressor);
11102             }
11103             var indexLeft = is_indexFn(self.left);
11104             if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
11105                 var node = make_node(AST_UnaryPrefix, self, {
11106                     operator: "!",
11107                     expression: make_node(AST_UnaryPrefix, self, {
11108                         operator: "~",
11109                         expression: indexLeft ? self.left : self.right
11110                     })
11111                 });
11112                 switch (self.operator) {
11113                   case "<":
11114                     if (indexLeft) break;
11115                   case "<=":
11116                   case "!=":
11117                     node = make_node(AST_UnaryPrefix, self, {
11118                         operator: "!",
11119                         expression: node
11120                     });
11121                     break;
11122                 }
11123                 return node.optimize(compressor);
11124             }
11125         }
11126         return try_evaluate(compressor, self);
11127
11128         function is_modify_array(node) {
11129             var found = false;
11130             node.walk(new TreeWalker(function(node) {
11131                 if (found) return true;
11132                 if (node instanceof AST_Assign) {
11133                     if (node.left instanceof AST_PropAccess) return found = true;
11134                 } else if (node instanceof AST_Unary) {
11135                     if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) {
11136                         return found = true;
11137                     }
11138                 }
11139             }));
11140             return found;
11141         }
11142
11143         function align(ref, op) {
11144             switch (ref) {
11145               case "-":
11146                 return op == "+" ? "-" : "+";
11147               case "/":
11148                 return op == "*" ? "/" : "*";
11149               default:
11150                 return op;
11151             }
11152         }
11153
11154         function make_binary(orig, op, left, right, start, end) {
11155             if (op == "+") {
11156                 if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
11157                     left = make_node(AST_UnaryPrefix, left, {
11158                         operator: "+",
11159                         expression: left
11160                     });
11161                 }
11162                 if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
11163                     right = make_node(AST_UnaryPrefix, right, {
11164                         operator: "+",
11165                         expression: right
11166                     });
11167                 }
11168             }
11169             return make_node(AST_Binary, orig, {
11170                 operator: op,
11171                 left: left,
11172                 right: right,
11173                 start: start,
11174                 end: end
11175             });
11176         }
11177
11178         function is_indexFn(node) {
11179             return node.TYPE == "Call"
11180                 && node.expression instanceof AST_Dot
11181                 && indexFns[node.expression.property];
11182         }
11183
11184         function is_indexOf_match_pattern() {
11185             switch (self.operator) {
11186               case "<=":
11187                 // 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
11188                 return indexRight && self.left instanceof AST_Number && self.left.value == 0;
11189               case "<":
11190                 // array.indexOf(string) < 0 ---> !~array.indexOf(string)
11191                 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
11192                 // -1 < array.indexOf(string) ---> !!~array.indexOf(string)
11193               case "==":
11194               case "!=":
11195                 // -1 == array.indexOf(string) ---> !~array.indexOf(string)
11196                 // -1 != array.indexOf(string) ---> !!~array.indexOf(string)
11197                 if (!indexRight) return false;
11198                 return self.left instanceof AST_Number && self.left.value == -1
11199                     || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
11200                         && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
11201             }
11202         }
11203     });
11204
11205     OPT(AST_SymbolExport, function(self) {
11206         return self;
11207     });
11208
11209     function recursive_ref(compressor, def, fn) {
11210         var level = 0, node = compressor.self();
11211         do {
11212             if (node === fn) return node;
11213             if (is_lambda(node) && node.name && node.name.definition() === def) return node;
11214         } while (node = compressor.parent(level++));
11215     }
11216
11217     function same_scope(def) {
11218         var scope = def.scope.resolve();
11219         return all(def.references, function(ref) {
11220             return scope === ref.scope.resolve();
11221         });
11222     }
11223
11224     OPT(AST_SymbolRef, function(self, compressor) {
11225         if (!compressor.option("ie")
11226             && is_undeclared_ref(self)
11227             // testing against `self.scope.uses_with` is an optimization
11228             && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
11229             switch (self.name) {
11230               case "undefined":
11231                 return make_node(AST_Undefined, self).optimize(compressor);
11232               case "NaN":
11233                 return make_node(AST_NaN, self).optimize(compressor);
11234               case "Infinity":
11235                 return make_node(AST_Infinity, self).optimize(compressor);
11236             }
11237         }
11238         var parent = compressor.parent();
11239         if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
11240             var def = self.definition();
11241             var fixed = self.fixed_value();
11242             var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
11243             if (single_use) {
11244                 if (is_lambda(fixed)) {
11245                     if ((def.scope !== self.scope.resolve() || def.in_loop)
11246                         && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
11247                         single_use = false;
11248                     } else if (recursive_ref(compressor, def, fixed)) {
11249                         single_use = false;
11250                     } else if (fixed.name && fixed.name.definition() !== def) {
11251                         single_use = false;
11252                     } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
11253                         single_use = fixed.is_constant_expression(self.scope);
11254                         if (single_use == "f") {
11255                             var scope = self.scope;
11256                             do {
11257                                 if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
11258                                     scope.inlined = true;
11259                                 }
11260                             } while (scope = scope.parent_scope);
11261                         }
11262                     } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
11263                         || fixed.name.name == "yield" && is_generator(fixed))) {
11264                         single_use = false;
11265                     } else if (fixed.has_side_effects(compressor)) {
11266                         single_use = false;
11267                     } else if (compressor.option("ie") && fixed instanceof AST_Class) {
11268                         single_use = false;
11269                     }
11270                     if (single_use) fixed.parent_scope = self.scope;
11271                 } else if (!fixed
11272                     || def.recursive_refs > 0
11273                     || !fixed.is_constant_expression()
11274                     || fixed.drop_side_effect_free(compressor)) {
11275                     single_use = false;
11276                 }
11277             }
11278             if (single_use) {
11279                 def.single_use = false;
11280                 fixed._squeezed = true;
11281                 fixed.single_use = true;
11282                 if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
11283                 if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
11284                 if (is_lambda(fixed)) {
11285                     var scope = self.scope.resolve();
11286                     fixed.enclosed.forEach(function(def) {
11287                         if (fixed.variables.has(def.name)) return;
11288                         if (scope.var_names()[def.name]) return;
11289                         scope.enclosed.push(def);
11290                         scope.var_names()[def.name] = true;
11291                     });
11292                 }
11293                 var value;
11294                 if (def.recursive_refs > 0) {
11295                     value = fixed.clone(true);
11296                     var defun_def = value.name.definition();
11297                     var lambda_def = value.variables.get(value.name.name);
11298                     var name = lambda_def && lambda_def.orig[0];
11299                     var def_fn_name, symbol_type;
11300                     if (value instanceof AST_Class) {
11301                         def_fn_name = "def_function";
11302                         symbol_type = AST_SymbolClass;
11303                     } else {
11304                         def_fn_name = "def_variable";
11305                         symbol_type = AST_SymbolLambda;
11306                     }
11307                     if (!(name instanceof symbol_type)) {
11308                         name = make_node(symbol_type, value.name, value.name);
11309                         name.scope = value;
11310                         value.name = name;
11311                         lambda_def = value[def_fn_name](name);
11312                         lambda_def.recursive_refs = def.recursive_refs;
11313                     }
11314                     value.walk(new TreeWalker(function(node) {
11315                         if (node instanceof AST_SymbolDeclaration) {
11316                             if (node !== name) {
11317                                 var def = node.definition();
11318                                 def.orig.push(node);
11319                                 def.eliminated++;
11320                             }
11321                             return;
11322                         }
11323                         if (!(node instanceof AST_SymbolRef)) return;
11324                         var def = node.definition();
11325                         if (def === defun_def) {
11326                             node.thedef = def = lambda_def;
11327                         } else {
11328                             def.single_use = false;
11329                             var fn = node.fixed_value();
11330                             if (is_lambda(fn)
11331                                 && fn.name
11332                                 && fn.name.definition() === def
11333                                 && def.scope === fn.name.scope
11334                                 && fixed.variables.get(fn.name.name) === def) {
11335                                 fn.name = fn.name.clone();
11336                                 node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
11337                             }
11338                         }
11339                         def.references.push(node);
11340                     }));
11341                 } else {
11342                     if (fixed instanceof AST_Scope) {
11343                         compressor.push(fixed);
11344                         value = fixed.optimize(compressor);
11345                         compressor.pop();
11346                     } else {
11347                         value = fixed.optimize(compressor);
11348                     }
11349                     value = value.transform(new TreeTransformer(function(node, descend) {
11350                         if (node instanceof AST_Scope) return node;
11351                         node = node.clone();
11352                         descend(node, this);
11353                         return node;
11354                     }));
11355                 }
11356                 def.replaced++;
11357                 return value;
11358             }
11359             var local = self.fixed !== def.fixed;
11360             if (fixed && (local || def.should_replace !== false)) {
11361                 var ev, init;
11362                 if (fixed instanceof AST_This) {
11363                     if (!is_funarg(def) && same_scope(def)) init = fixed;
11364                 } else if ((ev = fixed.evaluate(compressor, true)) !== fixed
11365                     && typeof ev != "function"
11366                     && (ev === null
11367                         || typeof ev != "object"
11368                         || compressor.option("unsafe_regexp")
11369                             && ev instanceof RegExp && !def.cross_loop && same_scope(def))) {
11370                     init = make_node_from_constant(ev, fixed);
11371                 }
11372                 if (init) {
11373                     if (!local && def.should_replace === undefined) {
11374                         var value_length = init.optimize(compressor).print_to_string().length;
11375                         if (!has_symbol_ref(fixed)) {
11376                             value_length = Math.min(value_length, fixed.print_to_string().length);
11377                         }
11378                         var name_length = def.name.length;
11379                         if (compressor.option("unused") && !compressor.exposed(def)) {
11380                             var referenced = def.references.length - def.replaced;
11381                             name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
11382                         }
11383                         var delta = value_length - Math.floor(name_length);
11384                         def.should_replace = delta < compressor.eval_threshold;
11385                     }
11386                     if (local || def.should_replace) {
11387                         var value;
11388                         if (has_symbol_ref(fixed)) {
11389                             value = init.optimize(compressor);
11390                             if (value === init) value = value.clone(true);
11391                         } else {
11392                             value = best_of_expression(init.optimize(compressor), fixed);
11393                             if (value === init || value === fixed) value = value.clone(true);
11394                         }
11395                         def.replaced++;
11396                         return value;
11397                     }
11398                 }
11399             }
11400         }
11401         return self;
11402
11403         function has_symbol_ref(value) {
11404             var found;
11405             value.walk(new TreeWalker(function(node) {
11406                 if (node instanceof AST_SymbolRef) found = true;
11407                 if (found) return true;
11408             }));
11409             return found;
11410         }
11411     });
11412
11413     function is_raw_tag(compressor, tag) {
11414         return compressor.option("unsafe")
11415             && tag instanceof AST_Dot
11416             && tag.property == "raw"
11417             && is_undeclared_ref(tag.expression)
11418             && tag.expression.name == "String";
11419     }
11420
11421     function decode_template(str) {
11422         var malformed = false;
11423         str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
11424             var ch = decode_escape_sequence(seq);
11425             if (typeof ch == "string") return ch;
11426             malformed = true;
11427         });
11428         if (!malformed) return str;
11429     }
11430
11431     OPT(AST_Template, function(self, compressor) {
11432         if (!compressor.option("templates")) return self;
11433         var tag = self.tag;
11434         if (!tag || is_raw_tag(compressor, tag)) {
11435             var exprs = [];
11436             var strs = [];
11437             for (var i = 0, status; i < self.strings.length; i++) {
11438                 var str = self.strings[i];
11439                 if (!tag) {
11440                     var trimmed = decode_template(str);
11441                     if (trimmed) str = escape_literal(trimmed);
11442                 }
11443                 if (i > 0) {
11444                     var node = self.expressions[i - 1];
11445                     var value = should_join(node);
11446                     if (value) {
11447                         var prev = strs[strs.length - 1];
11448                         var joined = prev + value + str;
11449                         var decoded;
11450                         if (tag || typeof (decoded = decode_template(joined)) == status) {
11451                             strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined;
11452                             continue;
11453                         }
11454                     }
11455                     exprs.push(node);
11456                 }
11457                 strs.push(str);
11458                 if (!tag) status = typeof trimmed;
11459             }
11460             if (!tag && strs.length > 1) {
11461                 if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, {
11462                     operator: "+",
11463                     left: make_node(AST_Template, self, {
11464                         expressions: exprs.slice(0, -1),
11465                         strings: strs.slice(0, -1),
11466                         tag: tag,
11467                     }).transform(compressor),
11468                     right: exprs[exprs.length - 1],
11469                 }).optimize(compressor);
11470                 if (strs[0] == "") {
11471                     var left = make_node(AST_Binary, self, {
11472                         operator: "+",
11473                         left: make_node(AST_String, self, { value: "" }),
11474                         right: exprs[0],
11475                     });
11476                     for (var i = 1; strs[i] == "" && i < exprs.length; i++) {
11477                         left = make_node(AST_Binary, self, {
11478                             operator: "+",
11479                             left: left,
11480                             right: exprs[i],
11481                         });
11482                     }
11483                     return best_of(compressor, self, make_node(AST_Binary, self, {
11484                         operator: "+",
11485                         left: left.transform(compressor),
11486                         right: make_node(AST_Template, self, {
11487                             expressions: exprs.slice(i),
11488                             strings: strs.slice(i),
11489                             tag: tag,
11490                         }).transform(compressor),
11491                     }).optimize(compressor));
11492                 }
11493             }
11494             self.expressions = exprs;
11495             self.strings = strs;
11496         }
11497         return try_evaluate(compressor, self);
11498
11499         function escape_literal(str) {
11500             return str.replace(/\r|\\|`|\${/g, function(s) {
11501                 return "\\" + (s == "\r" ? "r" : s);
11502             });
11503         }
11504
11505         function should_join(node) {
11506             var ev = node.evaluate(compressor);
11507             if (ev === node) return;
11508             if (tag && /\r|\\|`/.test(ev)) return;
11509             ev = escape_literal("" + ev);
11510             if (ev.length > node.print_to_string().length + "${}".length) return;
11511             return ev;
11512         }
11513     });
11514
11515     function is_atomic(lhs, self) {
11516         return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
11517     }
11518
11519     OPT(AST_Undefined, function(self, compressor) {
11520         if (compressor.option("unsafe_undefined")) {
11521             var undef = find_scope(compressor).find_variable("undefined");
11522             if (undef) {
11523                 var ref = make_node(AST_SymbolRef, self, {
11524                     name   : "undefined",
11525                     scope  : undef.scope,
11526                     thedef : undef
11527                 });
11528                 ref.is_undefined = true;
11529                 return ref;
11530             }
11531         }
11532         var lhs = is_lhs(compressor.self(), compressor.parent());
11533         if (lhs && is_atomic(lhs, self)) return self;
11534         return make_node(AST_UnaryPrefix, self, {
11535             operator: "void",
11536             expression: make_node(AST_Number, self, {
11537                 value: 0
11538             })
11539         });
11540     });
11541
11542     OPT(AST_Infinity, function(self, compressor) {
11543         var lhs = is_lhs(compressor.self(), compressor.parent());
11544         if (lhs && is_atomic(lhs, self)) return self;
11545         if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) {
11546             return self;
11547         }
11548         return make_node(AST_Binary, self, {
11549             operator: "/",
11550             left: make_node(AST_Number, self, {
11551                 value: 1
11552             }),
11553             right: make_node(AST_Number, self, {
11554                 value: 0
11555             })
11556         });
11557     });
11558
11559     OPT(AST_NaN, function(self, compressor) {
11560         var lhs = is_lhs(compressor.self(), compressor.parent());
11561         if (lhs && is_atomic(lhs, self)) return self;
11562         if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
11563         return make_node(AST_Binary, self, {
11564             operator: "/",
11565             left: make_node(AST_Number, self, {
11566                 value: 0
11567             }),
11568             right: make_node(AST_Number, self, {
11569                 value: 0
11570             })
11571         });
11572     });
11573
11574     function is_reachable(self, defs) {
11575         var reachable = false;
11576         var find_ref = new TreeWalker(function(node) {
11577             if (reachable) return true;
11578             if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
11579                 return reachable = true;
11580             }
11581         });
11582         var scan_scope = new TreeWalker(function(node) {
11583             if (reachable) return true;
11584             if (node instanceof AST_Lambda && node !== self) {
11585                 if (!(node.name || is_async(node) || is_generator(node))) {
11586                     var parent = scan_scope.parent();
11587                     if (parent instanceof AST_Call && parent.expression === node) return;
11588                 }
11589                 node.walk(find_ref);
11590                 return true;
11591             }
11592         });
11593         self.walk(scan_scope);
11594         return reachable;
11595     }
11596
11597     var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
11598     var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
11599     OPT(AST_Assign, function(self, compressor) {
11600         if (compressor.option("dead_code")) {
11601             if (self.left instanceof AST_PropAccess) {
11602                 if (self.operator == "=") {
11603                     if (self.__drop) {
11604                         var exprs = [ self.left.expression ];
11605                         if (self.left instanceof AST_Sub) exprs.push(self.left.property);
11606                         exprs.push(self.right);
11607                         return make_sequence(self, exprs).optimize(compressor);
11608                     }
11609                     if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
11610                         return self.right;
11611                     }
11612                     var exp = self.left.expression;
11613                     if (exp instanceof AST_Lambda
11614                         || !compressor.has_directive("use strict")
11615                             && exp instanceof AST_Constant
11616                             && !exp.may_throw_on_access(compressor)) {
11617                         return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
11618                             self.left.property,
11619                             self.right
11620                         ]).optimize(compressor);
11621                     }
11622                 }
11623             } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) {
11624                 var parent;
11625                 if (self.operator == "=" && self.left.equivalent_to(self.right)
11626                     && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11627                     return self.right;
11628                 }
11629                 if (self.left.is_immutable()) return strip_assignment();
11630                 var def = self.left.definition();
11631                 var scope = def.scope.resolve();
11632                 var local = scope === compressor.find_parent(AST_Lambda);
11633                 var level = 0, node;
11634                 parent = compressor.self();
11635                 if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do {
11636                     node = parent;
11637                     parent = compressor.parent(level++);
11638                     if (parent instanceof AST_Assign) {
11639                         if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) {
11640                             if (in_try(level, parent)) break;
11641                             return strip_assignment(def);
11642                         }
11643                         if (parent.left.match_symbol(function(node) {
11644                             if (node instanceof AST_PropAccess) return true;
11645                         })) break;
11646                         continue;
11647                     }
11648                     if (parent instanceof AST_Exit) {
11649                         if (!local) break;
11650                         if (in_try(level, parent)) break;
11651                         if (is_reachable(scope, [ def ])) break;
11652                         return strip_assignment(def);
11653                     }
11654                     if (parent instanceof AST_SimpleStatement) {
11655                         if (!local) break;
11656                         if (is_reachable(scope, [ def ])) break;
11657                         var stat;
11658                         do {
11659                             stat = parent;
11660                             parent = compressor.parent(level++);
11661                             if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def);
11662                         } while (is_tail_block(stat, parent));
11663                         break;
11664                     }
11665                     if (parent instanceof AST_VarDef) {
11666                         if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
11667                         if (parent.name.definition() !== def) continue;
11668                         if (in_try(level, parent)) break;
11669                         return strip_assignment(def);
11670                     }
11671                 } while (is_tail(node, parent));
11672             }
11673         }
11674         if (compressor.option("sequences")) {
11675             var seq = self.lift_sequences(compressor);
11676             if (seq !== self) return seq.optimize(compressor);
11677         }
11678         if (compressor.option("assignments")) {
11679             if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
11680                 // x = expr1 OP expr2
11681                 if (self.right.left instanceof AST_SymbolRef
11682                     && self.right.left.name == self.left.name
11683                     && ASSIGN_OPS[self.right.operator]) {
11684                     // x = x - 2 ---> x -= 2
11685                     return make_node(AST_Assign, self, {
11686                         operator: self.right.operator + "=",
11687                         left: self.left,
11688                         right: self.right.right,
11689                     });
11690                 }
11691                 if (self.right.right instanceof AST_SymbolRef
11692                     && self.right.right.name == self.left.name
11693                     && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
11694                     && !self.right.left.has_side_effects(compressor)) {
11695                     // x = 2 & x ---> x &= 2
11696                     return make_node(AST_Assign, self, {
11697                         operator: self.right.operator + "=",
11698                         left: self.left,
11699                         right: self.right.left,
11700                     });
11701                 }
11702             }
11703             if ((self.operator == "-=" || self.operator == "+="
11704                     && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
11705                 && self.right instanceof AST_Number
11706                 && self.right.value == 1) {
11707                 var op = self.operator.slice(0, -1);
11708                 return make_node(AST_UnaryPrefix, self, {
11709                     operator: op + op,
11710                     expression: self.left
11711                 });
11712             }
11713         }
11714         return try_evaluate(compressor, self);
11715
11716         function is_tail(node, parent) {
11717             if (parent instanceof AST_Binary) {
11718                 return parent.right === node || parent.right.is_constant_expression(scope);
11719             }
11720             if (parent instanceof AST_Conditional) {
11721                 return parent.condition !== node
11722                     || parent.consequent.is_constant_expression(scope)
11723                         && parent.alternative.is_constant_expression(scope);
11724             }
11725             if (parent instanceof AST_Sequence) {
11726                 var exprs = parent.expressions;
11727                 var stop = exprs.indexOf(node);
11728                 if (stop < 0) return false;
11729                 for (var i = exprs.length; --i > stop;) {
11730                     if (!exprs[i].is_constant_expression(scope)) return false;
11731                 }
11732                 return true;
11733             }
11734             if (parent instanceof AST_UnaryPrefix) return true;
11735         }
11736
11737         function is_tail_block(stat, parent) {
11738             if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat);
11739             if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat);
11740             if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat);
11741             if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat;
11742             if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
11743         }
11744
11745         function in_try(level, node) {
11746             var right = self.right;
11747             self.right = make_node(AST_Null, right);
11748             var may_throw = node.may_throw(compressor);
11749             self.right = right;
11750             for (var parent; parent = compressor.parent(level++); node = parent) {
11751                 if (parent === scope) return false;
11752                 if (parent instanceof AST_Try) {
11753                     if (parent.bfinally && parent.bfinally !== node) return true;
11754                     if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
11755                 }
11756             }
11757         }
11758
11759         function strip_assignment(def) {
11760             if (def) def.fixed = false;
11761             return (self.operator != "=" ? make_node(AST_Binary, self, {
11762                 operator: self.operator.slice(0, -1),
11763                 left: self.left,
11764                 right: self.right,
11765             }) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
11766         }
11767     });
11768
11769     OPT(AST_Conditional, function(self, compressor) {
11770         if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
11771             var expressions = self.condition.expressions.slice();
11772             self.condition = expressions.pop();
11773             expressions.push(self);
11774             return make_sequence(self, expressions);
11775         }
11776         if (!compressor.option("conditionals")) return self;
11777         var condition = self.condition;
11778         if (compressor.option("booleans") && !condition.has_side_effects(compressor)) {
11779             mark_duplicate_condition(compressor, condition);
11780         }
11781         condition = fuzzy_eval(compressor, condition);
11782         if (!condition) {
11783             AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
11784             return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
11785         } else if (!(condition instanceof AST_Node)) {
11786             AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
11787             return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
11788         }
11789         var negated = condition.negate(compressor, first_in_statement(compressor));
11790         if (best_of(compressor, condition, negated) === negated) {
11791             self = make_node(AST_Conditional, self, {
11792                 condition: negated,
11793                 consequent: self.alternative,
11794                 alternative: self.consequent
11795             });
11796             negated = condition;
11797             condition = self.condition;
11798         }
11799         var consequent = self.consequent;
11800         var alternative = self.alternative;
11801         if (repeatable(compressor, condition)) {
11802             // x ? x : y ---> x || y
11803             if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
11804                 operator: "||",
11805                 left: condition,
11806                 right: alternative,
11807             }).optimize(compressor);
11808             // x ? y : x ---> x && y
11809             if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
11810                 operator: "&&",
11811                 left: condition,
11812                 right: consequent,
11813             }).optimize(compressor);
11814         }
11815         // if (foo) exp = something; else exp = something_else;
11816         //                   |
11817         //                   v
11818         // exp = foo ? something : something_else;
11819         var seq_tail = consequent.tail_node();
11820         if (seq_tail instanceof AST_Assign) {
11821             var is_eq = seq_tail.operator == "=";
11822             var alt_tail = is_eq ? alternative.tail_node() : alternative;
11823             if ((is_eq || consequent === seq_tail)
11824                 && alt_tail instanceof AST_Assign
11825                 && seq_tail.operator == alt_tail.operator
11826                 && seq_tail.left.equivalent_to(alt_tail.left)
11827                 && (is_eq && seq_tail.left instanceof AST_SymbolRef
11828                     || !condition.has_side_effects(compressor)
11829                         && can_shift_lhs_of_tail(consequent)
11830                         && can_shift_lhs_of_tail(alternative))) {
11831                 return make_node(AST_Assign, self, {
11832                     operator: seq_tail.operator,
11833                     left: seq_tail.left,
11834                     right: make_node(AST_Conditional, self, {
11835                         condition: condition,
11836                         consequent: pop_lhs(consequent),
11837                         alternative: pop_lhs(alternative)
11838                     })
11839                 });
11840             }
11841         }
11842         // x ? y : y ---> x, y
11843         if (consequent.equivalent_to(alternative)) return make_sequence(self, [
11844             condition,
11845             consequent
11846         ]).optimize(compressor);
11847         // x ? y.p : z.p ---> (x ? y : z).p
11848         // x ? y(a) : z(a) ---> (x ? y : z)(a)
11849         // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
11850         var combined = combine_tail(consequent, alternative, true);
11851         if (combined) return combined;
11852         // x ? y(a) : y(b) ---> y(x ? a : b)
11853         var arg_index;
11854         if (consequent instanceof AST_Call
11855             && alternative.TYPE == consequent.TYPE
11856             && (arg_index = arg_diff(consequent, alternative)) >= 0
11857             && consequent.expression.equivalent_to(alternative.expression)
11858             && !condition.has_side_effects(compressor)
11859             && !consequent.expression.has_side_effects(compressor)) {
11860             var node = consequent.clone();
11861             var arg = consequent.args[arg_index];
11862             node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, {
11863                 expression: make_node(AST_Conditional, self, {
11864                     condition: condition,
11865                     consequent: arg.expression,
11866                     alternative: alternative.args[arg_index].expression,
11867                 }),
11868             }) : make_node(AST_Conditional, self, {
11869                 condition: condition,
11870                 consequent: arg,
11871                 alternative: alternative.args[arg_index],
11872             });
11873             return node;
11874         }
11875         // x ? (y ? a : b) : b ---> x && y ? a : b
11876         if (consequent instanceof AST_Conditional
11877             && consequent.alternative.equivalent_to(alternative)) {
11878             return make_node(AST_Conditional, self, {
11879                 condition: make_node(AST_Binary, self, {
11880                     left: condition,
11881                     operator: "&&",
11882                     right: consequent.condition
11883                 }),
11884                 consequent: consequent.consequent,
11885                 alternative: alternative
11886             });
11887         }
11888         // x ? (y ? a : b) : a ---> !x || y ? a : b
11889         if (consequent instanceof AST_Conditional
11890             && consequent.consequent.equivalent_to(alternative)) {
11891             return make_node(AST_Conditional, self, {
11892                 condition: make_node(AST_Binary, self, {
11893                     left: negated,
11894                     operator: "||",
11895                     right: consequent.condition
11896                 }),
11897                 consequent: alternative,
11898                 alternative: consequent.alternative
11899             });
11900         }
11901         // x ? a : (y ? a : b) ---> x || y ? a : b
11902         if (alternative instanceof AST_Conditional
11903             && consequent.equivalent_to(alternative.consequent)) {
11904             return make_node(AST_Conditional, self, {
11905                 condition: make_node(AST_Binary, self, {
11906                     left: condition,
11907                     operator: "||",
11908                     right: alternative.condition
11909                 }),
11910                 consequent: consequent,
11911                 alternative: alternative.alternative
11912             });
11913         }
11914         // x ? b : (y ? a : b) ---> !x && y ? a : b
11915         if (alternative instanceof AST_Conditional
11916             && consequent.equivalent_to(alternative.alternative)) {
11917             return make_node(AST_Conditional, self, {
11918                 condition: make_node(AST_Binary, self, {
11919                     left: negated,
11920                     operator: "&&",
11921                     right: alternative.condition
11922                 }),
11923                 consequent: alternative.consequent,
11924                 alternative: consequent
11925             });
11926         }
11927         // x ? (a, c) : (b, c) ---> x ? a : b, c
11928         if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
11929             && consequent.tail_node().equivalent_to(alternative.tail_node())) {
11930             return make_sequence(self, [
11931                 make_node(AST_Conditional, self, {
11932                     condition: condition,
11933                     consequent: pop_seq(consequent),
11934                     alternative: pop_seq(alternative)
11935                 }),
11936                 consequent.tail_node()
11937             ]).optimize(compressor);
11938         }
11939         // x ? y && a : a ---> (!x || y) && a
11940         if (consequent instanceof AST_Binary
11941             && consequent.operator == "&&"
11942             && consequent.right.equivalent_to(alternative)) {
11943             return make_node(AST_Binary, self, {
11944                 operator: "&&",
11945                 left: make_node(AST_Binary, self, {
11946                     operator: "||",
11947                     left: negated,
11948                     right: consequent.left
11949                 }),
11950                 right: alternative
11951             }).optimize(compressor);
11952         }
11953         // x ? y || a : a ---> x && y || a
11954         if (consequent instanceof AST_Binary
11955             && consequent.operator == "||"
11956             && consequent.right.equivalent_to(alternative)) {
11957             return make_node(AST_Binary, self, {
11958                 operator: "||",
11959                 left: make_node(AST_Binary, self, {
11960                     operator: "&&",
11961                     left: condition,
11962                     right: consequent.left
11963                 }),
11964                 right: alternative
11965             }).optimize(compressor);
11966         }
11967         // x ? a : y && a ---> (x || y) && a
11968         if (alternative instanceof AST_Binary
11969             && alternative.operator == "&&"
11970             && alternative.right.equivalent_to(consequent)) {
11971             return make_node(AST_Binary, self, {
11972                 operator: "&&",
11973                 left: make_node(AST_Binary, self, {
11974                     operator: "||",
11975                     left: condition,
11976                     right: alternative.left
11977                 }),
11978                 right: consequent
11979             }).optimize(compressor);
11980         }
11981         // x ? a : y || a ---> !x && y || a
11982         if (alternative instanceof AST_Binary
11983             && alternative.operator == "||"
11984             && alternative.right.equivalent_to(consequent)) {
11985             return make_node(AST_Binary, self, {
11986                 operator: "||",
11987                 left: make_node(AST_Binary, self, {
11988                     operator: "&&",
11989                     left: negated,
11990                     right: alternative.left
11991                 }),
11992                 right: consequent
11993             }).optimize(compressor);
11994         }
11995         var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
11996         if (is_true(consequent)) {
11997             if (is_false(alternative)) {
11998                 // c ? true : false ---> !!c
11999                 return booleanize(condition);
12000             }
12001             // c ? true : x ---> !!c || x
12002             return make_node(AST_Binary, self, {
12003                 operator: "||",
12004                 left: booleanize(condition),
12005                 right: alternative
12006             });
12007         }
12008         if (is_false(consequent)) {
12009             if (is_true(alternative)) {
12010                 // c ? false : true ---> !c
12011                 return booleanize(condition.negate(compressor));
12012             }
12013             // c ? false : x ---> !c && x
12014             return make_node(AST_Binary, self, {
12015                 operator: "&&",
12016                 left: booleanize(condition.negate(compressor)),
12017                 right: alternative
12018             });
12019         }
12020         if (is_true(alternative)) {
12021             // c ? x : true ---> !c || x
12022             return make_node(AST_Binary, self, {
12023                 operator: "||",
12024                 left: booleanize(condition.negate(compressor)),
12025                 right: consequent
12026             });
12027         }
12028         if (is_false(alternative)) {
12029             // c ? x : false ---> !!c && x
12030             return make_node(AST_Binary, self, {
12031                 operator: "&&",
12032                 left: booleanize(condition),
12033                 right: consequent
12034             });
12035         }
12036         if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
12037         return self;
12038
12039         function booleanize(node) {
12040             if (node.is_boolean(compressor)) return node;
12041             // !!expression
12042             return make_node(AST_UnaryPrefix, node, {
12043                 operator: "!",
12044                 expression: node.negate(compressor)
12045             });
12046         }
12047
12048         // AST_True or !0
12049         function is_true(node) {
12050             return node instanceof AST_True
12051                 || in_bool
12052                     && node instanceof AST_Constant
12053                     && node.value
12054                 || (node instanceof AST_UnaryPrefix
12055                     && node.operator == "!"
12056                     && node.expression instanceof AST_Constant
12057                     && !node.expression.value);
12058         }
12059         // AST_False or !1 or void 0
12060         function is_false(node) {
12061             return node instanceof AST_False
12062                 || in_bool
12063                     && (node instanceof AST_Constant
12064                             && !node.value
12065                         || node instanceof AST_UnaryPrefix
12066                             && node.operator == "void"
12067                             && !node.expression.has_side_effects(compressor))
12068                 || (node instanceof AST_UnaryPrefix
12069                     && node.operator == "!"
12070                     && node.expression instanceof AST_Constant
12071                     && node.expression.value);
12072         }
12073
12074         function arg_diff(consequent, alternative) {
12075             var a = consequent.args;
12076             var b = alternative.args;
12077             var len = a.length;
12078             if (len != b.length) return -2;
12079             for (var i = 0; i < len; i++) {
12080                 if (!a[i].equivalent_to(b[i])) {
12081                     if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
12082                     for (var j = i + 1; j < len; j++) {
12083                         if (!a[j].equivalent_to(b[j])) return -2;
12084                     }
12085                     return i;
12086                 }
12087             }
12088             return -1;
12089         }
12090
12091         function is_tail_equivalent(consequent, alternative) {
12092             if (consequent.TYPE != alternative.TYPE) return;
12093             if (consequent.optional != alternative.optional) return;
12094             if (consequent instanceof AST_Call) {
12095                 if (arg_diff(consequent, alternative) != -1) return;
12096                 return consequent.TYPE != "Call"
12097                     || !(consequent.expression instanceof AST_PropAccess
12098                         || alternative.expression instanceof AST_PropAccess)
12099                     || is_tail_equivalent(consequent.expression, alternative.expression);
12100             }
12101             if (!(consequent instanceof AST_PropAccess)) return;
12102             var p = consequent.property;
12103             var q = alternative.property;
12104             return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
12105                 && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
12106         }
12107
12108         function combine_tail(consequent, alternative, top) {
12109             if (!is_tail_equivalent(consequent, alternative)) return !top && make_node(AST_Conditional, self, {
12110                 condition: condition,
12111                 consequent: consequent,
12112                 alternative: alternative,
12113             });
12114             var node = consequent.clone();
12115             node.expression = combine_tail(consequent.expression, alternative.expression);
12116             return node;
12117         }
12118
12119         function can_shift_lhs_of_tail(node) {
12120             return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
12121                 return !expr.has_side_effects(compressor);
12122             });
12123         }
12124
12125         function pop_lhs(node) {
12126             if (!(node instanceof AST_Sequence)) return node.right;
12127             var exprs = node.expressions.slice();
12128             exprs.push(exprs.pop().right);
12129             return make_sequence(node, exprs);
12130         }
12131
12132         function pop_seq(node) {
12133             if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
12134                 value: 0
12135             });
12136             return make_sequence(node, node.expressions.slice(0, -1));
12137         }
12138     });
12139
12140     OPT(AST_Boolean, function(self, compressor) {
12141         if (!compressor.option("booleans")) return self;
12142         if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
12143             value: +self.value
12144         });
12145         var p = compressor.parent();
12146         if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
12147             AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
12148                 operator : p.operator,
12149                 value    : self.value,
12150                 file     : p.start.file,
12151                 line     : p.start.line,
12152                 col      : p.start.col,
12153             });
12154             return make_node(AST_Number, self, {
12155                 value: +self.value
12156             });
12157         }
12158         return make_node(AST_UnaryPrefix, self, {
12159             operator: "!",
12160             expression: make_node(AST_Number, self, {
12161                 value: 1 - self.value
12162             })
12163         });
12164     });
12165
12166     OPT(AST_Spread, function(self, compressor) {
12167         var exp = self.expression;
12168         if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
12169             return List.splice(exp.elements.map(function(node) {
12170                 return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
12171             }));
12172         }
12173         return self;
12174     });
12175
12176     function safe_to_flatten(value, compressor) {
12177         if (!value) return false;
12178         var parent = compressor.parent();
12179         if (parent.TYPE != "Call") return true;
12180         if (parent.expression !== compressor.self()) return true;
12181         if (value instanceof AST_SymbolRef) {
12182             value = value.fixed_value();
12183             if (!value) return false;
12184         }
12185         return value instanceof AST_Lambda && !value.contains_this();
12186     }
12187
12188     OPT(AST_Sub, function(self, compressor) {
12189         var expr = self.expression;
12190         var prop = self.property;
12191         var terminated = trim_optional_chain(self, compressor);
12192         if (terminated) return terminated;
12193         if (compressor.option("properties")) {
12194             var key = prop.evaluate(compressor);
12195             if (key !== prop) {
12196                 if (typeof key == "string") {
12197                     if (key == "undefined") {
12198                         key = undefined;
12199                     } else {
12200                         var value = parseFloat(key);
12201                         if (value.toString() == key) {
12202                             key = value;
12203                         }
12204                     }
12205                 }
12206                 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
12207                 var property = "" + key;
12208                 if (is_identifier_string(property)
12209                     && property.length <= prop.print_to_string().length + 1) {
12210                     return make_node(AST_Dot, self, {
12211                         optional: self.optional,
12212                         expression: expr,
12213                         property: property,
12214                     }).optimize(compressor);
12215                 }
12216             }
12217         }
12218         var parent = compressor.parent();
12219         var assigned = is_lhs(compressor.self(), parent);
12220         var def, fn, fn_parent, index;
12221         if (compressor.option("arguments")
12222             && expr instanceof AST_SymbolRef
12223             && is_arguments(def = expr.definition())
12224             && !expr.in_arg
12225             && prop instanceof AST_Number
12226             && Math.floor(index = prop.value) == index
12227             && (fn = def.scope) === find_lambda()
12228             && fn.uses_arguments < (assigned ? 2 : 3)) {
12229             if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
12230                 if (!def.deleted) def.deleted = [];
12231                 def.deleted[index] = true;
12232             }
12233             var argname = fn.argnames[index];
12234             if (def.deleted && def.deleted[index]) {
12235                 argname = null;
12236             } else if (argname) {
12237                 var arg_def;
12238                 if (!(argname instanceof AST_SymbolFunarg)
12239                     || argname.name == "await"
12240                     || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) {
12241                     argname = null;
12242                 } else if (compressor.has_directive("use strict")
12243                     || fn.name
12244                     || fn.rest
12245                     || !(fn_parent instanceof AST_Call
12246                         && index < fn_parent.args.length
12247                         && all(fn_parent.args.slice(0, index + 1), function(arg) {
12248                             return !(arg instanceof AST_Spread);
12249                         }))
12250                     || !all(fn.argnames, function(argname) {
12251                         return argname instanceof AST_SymbolFunarg;
12252                     })) {
12253                     if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
12254                 }
12255             } else if ((assigned || !has_reassigned())
12256                 && index < fn.argnames.length + 5
12257                 && compressor.drop_fargs(fn, fn_parent)
12258                 && !fn.rest) {
12259                 while (index >= fn.argnames.length) {
12260                     argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
12261                     fn.argnames.push(argname);
12262                 }
12263             }
12264             if (argname && find_if(function(node) {
12265                 return node.name === argname.name;
12266             }, fn.argnames) === argname) {
12267                 if (assigned) def.reassigned--;
12268                 var sym = make_node(AST_SymbolRef, self, argname);
12269                 sym.reference();
12270                 delete argname.__unused;
12271                 return sym;
12272             }
12273         }
12274         if (assigned) return self;
12275         if (compressor.option("sequences")
12276             && parent.TYPE != "Call"
12277             && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12278             var seq = lift_sequence_in_expression(self, compressor);
12279             if (seq !== self) return seq.optimize(compressor);
12280         }
12281         if (key !== prop) {
12282             var sub = self.flatten_object(property, compressor);
12283             if (sub) {
12284                 expr = self.expression = sub.expression;
12285                 prop = self.property = sub.property;
12286             }
12287         }
12288         var elements;
12289         if (compressor.option("properties")
12290             && compressor.option("side_effects")
12291             && prop instanceof AST_Number
12292             && expr instanceof AST_Array
12293             && all(elements = expr.elements, function(value) {
12294                 return !(value instanceof AST_Spread);
12295             })) {
12296             var index = prop.value;
12297             var retValue = elements[index];
12298             if (safe_to_flatten(retValue, compressor)) {
12299                 var is_hole = retValue instanceof AST_Hole;
12300                 var flatten = !is_hole;
12301                 var values = [];
12302                 for (var i = elements.length; --i > index;) {
12303                     var value = elements[i].drop_side_effect_free(compressor);
12304                     if (value) {
12305                         values.unshift(value);
12306                         if (flatten && value.has_side_effects(compressor)) flatten = false;
12307                     }
12308                 }
12309                 if (!flatten) values.unshift(retValue);
12310                 while (--i >= 0) {
12311                     var value = elements[i].drop_side_effect_free(compressor);
12312                     if (value) {
12313                         values.unshift(value);
12314                     } else if (is_hole) {
12315                         values.unshift(make_node(AST_Hole, elements[i]));
12316                     } else {
12317                         index--;
12318                     }
12319                 }
12320                 if (flatten) {
12321                     values.push(retValue);
12322                     return make_sequence(self, values).optimize(compressor);
12323                 } else return make_node(AST_Sub, self, {
12324                     expression: make_node(AST_Array, expr, { elements: values }),
12325                     property: make_node(AST_Number, prop, { value: index }),
12326                 });
12327             }
12328         }
12329         return try_evaluate(compressor, self);
12330
12331         function find_lambda() {
12332             var i = 0, p;
12333             while (p = compressor.parent(i++)) {
12334                 if (p instanceof AST_Lambda) {
12335                     if (p instanceof AST_Accessor) return;
12336                     if (is_arrow(p)) continue;
12337                     fn_parent = compressor.parent(i);
12338                     return p;
12339                 }
12340             }
12341         }
12342
12343         function has_reassigned() {
12344             return !compressor.option("reduce_vars") || def.reassigned;
12345         }
12346     });
12347
12348     AST_Arrow.DEFMETHOD("contains_super", return_false);
12349     AST_AsyncArrow.DEFMETHOD("contains_super", return_false);
12350     AST_Lambda.DEFMETHOD("contains_super", function() {
12351         var result;
12352         var self = this;
12353         self.walk(new TreeWalker(function(node) {
12354             if (result) return true;
12355             if (node instanceof AST_Super) return result = true;
12356             if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12357         }));
12358         return result;
12359     });
12360     AST_LambdaDefinition.DEFMETHOD("contains_super", return_false);
12361     AST_Scope.DEFMETHOD("contains_super", return_false);
12362
12363     AST_Arrow.DEFMETHOD("contains_this", return_false);
12364     AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
12365     AST_Node.DEFMETHOD("contains_this", function() {
12366         var result;
12367         var self = this;
12368         self.walk(new TreeWalker(function(node) {
12369             if (result) return true;
12370             if (node instanceof AST_This) return result = true;
12371             if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12372         }));
12373         return result;
12374     });
12375
12376     function can_hoist_property(prop) {
12377         return prop instanceof AST_ObjectKeyVal
12378             && typeof prop.key == "string"
12379             && !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
12380     }
12381
12382     AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
12383         if (!compressor.option("properties")) return;
12384         if (key === "__proto__") return;
12385         var expr = this.expression;
12386         if (expr instanceof AST_Object) {
12387             var props = expr.properties;
12388             for (var i = props.length; --i >= 0;) {
12389                 var prop = props[i];
12390                 if (prop.key !== key) continue;
12391                 if (!all(props, can_hoist_property)) return;
12392                 if (!safe_to_flatten(prop.value, compressor)) return;
12393                 var scope, values = [];
12394                 for (var j = 0; j < props.length; j++) {
12395                     var value = props[j].value;
12396                     if (props[j] instanceof AST_ObjectMethod) {
12397                         var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
12398                         if (arrow) {
12399                             if (!scope) scope = compressor.find_parent(AST_Scope);
12400                             var avoid = avoid_await_yield(scope);
12401                             value.each_argname(function(argname) {
12402                                 if (avoid[argname.name]) arrow = false;
12403                             });
12404                         }
12405                         var ctor;
12406                         if (arrow) {
12407                             ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow;
12408                         } else if (i === j && !(compressor.parent() instanceof AST_Call)) {
12409                             return;
12410                         } else {
12411                             ctor = value.CTOR;
12412                         }
12413                         value = make_node(ctor, value, value);
12414                     }
12415                     values.push(value);
12416                 }
12417                 return make_node(AST_Sub, this, {
12418                     expression: make_node(AST_Array, expr, { elements: values }),
12419                     property: make_node(AST_Number, this, { value: i }),
12420                 });
12421             }
12422         }
12423     });
12424
12425     OPT(AST_Dot, function(self, compressor) {
12426         if (self.property == "arguments" || self.property == "caller") {
12427             AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
12428                 prop: self.property,
12429                 file: self.start.file,
12430                 line: self.start.line,
12431                 col: self.start.col,
12432             });
12433         }
12434         var parent = compressor.parent();
12435         if (is_lhs(compressor.self(), parent)) return self;
12436         var terminated = trim_optional_chain(self, compressor);
12437         if (terminated) return terminated;
12438         if (compressor.option("sequences")
12439             && parent.TYPE != "Call"
12440             && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12441             var seq = lift_sequence_in_expression(self, compressor);
12442             if (seq !== self) return seq.optimize(compressor);
12443         }
12444         if (compressor.option("unsafe_proto")
12445             && self.expression instanceof AST_Dot
12446             && self.expression.property == "prototype") {
12447             var exp = self.expression.expression;
12448             if (is_undeclared_ref(exp)) switch (exp.name) {
12449               case "Array":
12450                 self.expression = make_node(AST_Array, self.expression, {
12451                     elements: []
12452                 });
12453                 break;
12454               case "Function":
12455                 self.expression = make_node(AST_Function, self.expression, {
12456                     argnames: [],
12457                     body: []
12458                 }).init_vars(exp.scope);
12459                 break;
12460               case "Number":
12461                 self.expression = make_node(AST_Number, self.expression, {
12462                     value: 0
12463                 });
12464                 break;
12465               case "Object":
12466                 self.expression = make_node(AST_Object, self.expression, {
12467                     properties: []
12468                 });
12469                 break;
12470               case "RegExp":
12471                 self.expression = make_node(AST_RegExp, self.expression, {
12472                     value: /t/
12473                 });
12474                 break;
12475               case "String":
12476                 self.expression = make_node(AST_String, self.expression, {
12477                     value: ""
12478                 });
12479                 break;
12480             }
12481         }
12482         var sub = self.flatten_object(self.property, compressor);
12483         if (sub) return sub.optimize(compressor);
12484         return try_evaluate(compressor, self);
12485     });
12486
12487     OPT(AST_DestructuredArray, function(self, compressor) {
12488         if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) {
12489             return make_node(AST_DestructuredArray, self, {
12490                 elements: self.elements.concat(self.rest.elements),
12491                 rest: self.rest.rest,
12492             });
12493         }
12494         return self;
12495     });
12496
12497     OPT(AST_DestructuredKeyVal, function(self, compressor) {
12498         if (compressor.option("objects")) {
12499             var key = self.key;
12500             if (key instanceof AST_Node) {
12501                 key = key.evaluate(compressor);
12502                 if (key !== self.key) self.key = "" + key;
12503             }
12504         }
12505         return self;
12506     });
12507
12508     OPT(AST_Object, function(self, compressor) {
12509         if (!compressor.option("objects")) return self;
12510         var changed = false;
12511         var found = false;
12512         var generated = false;
12513         var keep_duplicate = compressor.has_directive("use strict");
12514         var keys = new Dictionary();
12515         var values = [];
12516         self.properties.forEach(function(prop) {
12517             if (!(prop instanceof AST_Spread)) return process(prop);
12518             found = true;
12519             var exp = prop.expression;
12520             if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
12521                 if (prop instanceof AST_ObjectGetter) return false;
12522                 if (prop instanceof AST_Spread) return false;
12523                 if (prop.key !== "__proto__") return true;
12524                 if (prop instanceof AST_ObjectSetter) return true;
12525                 return !prop.value.has_side_effects(compressor);
12526             })) {
12527                 changed = true;
12528                 exp.properties.forEach(function(prop) {
12529                     var key = prop.key;
12530                     var setter = prop instanceof AST_ObjectSetter;
12531                     if (key === "__proto__") {
12532                         if (!setter) return;
12533                         key = make_node_from_constant(key, prop);
12534                     }
12535                     process(setter ? make_node(AST_ObjectKeyVal, prop, {
12536                         key: key,
12537                         value: make_node(AST_Undefined, prop).optimize(compressor),
12538                     }) : prop);
12539                 });
12540             } else {
12541                 generated = true;
12542                 flush();
12543                 values.push(prop);
12544             }
12545         });
12546         flush();
12547         if (!changed) return self;
12548         if (found && generated && values.length == 1) {
12549             var value = values[0];
12550             if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
12551                 value.key = "" + value.key.value;
12552             }
12553         }
12554         return make_node(AST_Object, self, { properties: values });
12555
12556         function flush() {
12557             keys.each(function(props) {
12558                 if (props.length == 1) return values.push(props[0]);
12559                 changed = true;
12560                 var tail = keep_duplicate && !generated && props.pop();
12561                 values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
12562                     key: props[0].key,
12563                     value: make_sequence(self, props.map(function(prop) {
12564                         return prop.value;
12565                     }))
12566                 }));
12567                 if (tail) values.push(tail);
12568             });
12569             keys = new Dictionary();
12570         }
12571
12572         function process(prop) {
12573             var key = prop.key;
12574             if (key instanceof AST_Node) {
12575                 found = true;
12576                 key = key.evaluate(compressor);
12577                 if (key === prop.key || key === "__proto__") {
12578                     generated = true;
12579                 } else {
12580                     key = prop.key = "" + key;
12581                 }
12582             }
12583             if (can_hoist_property(prop)) {
12584                 if (prop.value.has_side_effects(compressor)) flush();
12585                 keys.add(key, prop);
12586             } else {
12587                 flush();
12588                 values.push(prop);
12589             }
12590             if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
12591                 generated = true;
12592                 if (keys.has(key)) prop = keys.get(key)[0];
12593                 prop.key = make_node(AST_Number, prop, { value: +key });
12594             }
12595         }
12596     });
12597
12598     OPT(AST_Return, function(self, compressor) {
12599         if (compressor.option("side_effects")
12600             && self.value
12601             && is_undefined(self.value, compressor)
12602             && !in_async_generator(compressor.find_parent(AST_Scope))) {
12603             self.value = null;
12604         }
12605         return self;
12606     });
12607 })(function(node, optimizer) {
12608     node.DEFMETHOD("optimize", function(compressor) {
12609         var self = this;
12610         if (self._optimized) return self;
12611         if (compressor.has_directive("use asm")) return self;
12612         var opt = optimizer(self, compressor);
12613         opt._optimized = true;
12614         return opt;
12615     });
12616 });