1/***********************************************************************
2
3  A JavaScript tokenizer / parser / beautifier / compressor.
4  https://github.com/mishoo/UglifyJS
5
6  -------------------------------- (C) ---------------------------------
7
8                           Author: Mihai Bazon
9                         <mihai.bazon@gmail.com>
10                       http://mihai.bazon.net/blog
11
12  Distributed under the BSD license:
13
14    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16    Redistribution and use in source and binary forms, with or without
17    modification, are permitted provided that the following conditions
18    are met:
19
20        * Redistributions of source code must retain the above
21          copyright notice, this list of conditions and the following
22          disclaimer.
23
24        * Redistributions in binary form must reproduce the above
25          copyright notice, this list of conditions and the following
26          disclaimer in the documentation and/or other materials
27          provided with the distribution.
28
29    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40    SUCH DAMAGE.
41
42 ***********************************************************************/
43
44"use strict";
45
46function Compressor(options, false_by_default) {
47    if (!(this instanceof Compressor))
48        return new Compressor(options, false_by_default);
49    TreeTransformer.call(this, this.before, this.after);
50    this.options = defaults(options, {
51        annotations     : !false_by_default,
52        arguments       : !false_by_default,
53        arrows          : !false_by_default,
54        assignments     : !false_by_default,
55        awaits          : !false_by_default,
56        booleans        : !false_by_default,
57        collapse_vars   : !false_by_default,
58        comparisons     : !false_by_default,
59        conditionals    : !false_by_default,
60        dead_code       : !false_by_default,
61        default_values  : !false_by_default,
62        directives      : !false_by_default,
63        drop_console    : false,
64        drop_debugger   : !false_by_default,
65        evaluate        : !false_by_default,
66        expression      : false,
67        functions       : !false_by_default,
68        global_defs     : false,
69        hoist_exports   : !false_by_default,
70        hoist_funs      : false,
71        hoist_props     : !false_by_default,
72        hoist_vars      : false,
73        ie              : false,
74        if_return       : !false_by_default,
75        imports         : !false_by_default,
76        inline          : !false_by_default,
77        join_vars       : !false_by_default,
78        keep_fargs      : false_by_default,
79        keep_fnames     : false,
80        keep_infinity   : false,
81        loops           : !false_by_default,
82        merge_vars      : !false_by_default,
83        module          : false,
84        negate_iife     : !false_by_default,
85        objects         : !false_by_default,
86        optional_chains : !false_by_default,
87        passes          : 1,
88        properties      : !false_by_default,
89        pure_funcs      : null,
90        pure_getters    : !false_by_default && "strict",
91        reduce_funcs    : !false_by_default,
92        reduce_vars     : !false_by_default,
93        rests           : !false_by_default,
94        sequences       : !false_by_default,
95        side_effects    : !false_by_default,
96        spreads         : !false_by_default,
97        strings         : !false_by_default,
98        switches        : !false_by_default,
99        templates       : !false_by_default,
100        top_retain      : null,
101        toplevel        : !!(options && (options["module"] || options["top_retain"])),
102        typeofs         : !false_by_default,
103        unsafe          : false,
104        unsafe_comps    : false,
105        unsafe_Function : false,
106        unsafe_math     : false,
107        unsafe_proto    : false,
108        unsafe_regexp   : false,
109        unsafe_undefined: false,
110        unused          : !false_by_default,
111        varify          : !false_by_default,
112        webkit          : false,
113        yields          : !false_by_default,
114    }, true);
115    var evaluate = this.options["evaluate"];
116    this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
117    var global_defs = this.options["global_defs"];
118    if (typeof global_defs == "object") for (var key in global_defs) {
119        if (/^@/.test(key) && HOP(global_defs, key)) {
120            global_defs[key.slice(1)] = parse(global_defs[key], { expression: true });
121        }
122    }
123    if (this.options["inline"] === true) this.options["inline"] = 4;
124    this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) {
125        if (lambda.length_read) return false;
126        var name = lambda.name;
127        if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
128        if (name.fixed_value() !== lambda) return false;
129        var def = name.definition();
130        if (def.direct_access) return false;
131        var escaped = def.escaped;
132        return escaped && escaped.depth != 1;
133    };
134    if (this.options["module"]) this.directives["use strict"] = true;
135    var pure_funcs = this.options["pure_funcs"];
136    if (typeof pure_funcs == "function") {
137        this.pure_funcs = pure_funcs;
138    } else if (typeof pure_funcs == "string") {
139        this.pure_funcs = function(node) {
140            var expr;
141            if (node instanceof AST_Call) {
142                expr = node.expression;
143            } else if (node instanceof AST_Template) {
144                expr = node.tag;
145            }
146            return !(expr && pure_funcs === expr.print_to_string());
147        };
148    } else if (Array.isArray(pure_funcs)) {
149        this.pure_funcs = function(node) {
150            var expr;
151            if (node instanceof AST_Call) {
152                expr = node.expression;
153            } else if (node instanceof AST_Template) {
154                expr = node.tag;
155            }
156            return !(expr && member(expr.print_to_string(), pure_funcs));
157        };
158    } else {
159        this.pure_funcs = return_true;
160    }
161    var sequences = this.options["sequences"];
162    this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
163    var top_retain = this.options["top_retain"];
164    if (top_retain instanceof RegExp) {
165        this.top_retain = function(def) {
166            return top_retain.test(def.name);
167        };
168    } else if (typeof top_retain == "function") {
169        this.top_retain = top_retain;
170    } else if (top_retain) {
171        if (typeof top_retain == "string") {
172            top_retain = top_retain.split(/,/);
173        }
174        this.top_retain = function(def) {
175            return member(def.name, top_retain);
176        };
177    }
178    var toplevel = this.options["toplevel"];
179    this.toplevel = typeof toplevel == "string" ? {
180        funcs: /funcs/.test(toplevel),
181        vars: /vars/.test(toplevel)
182    } : {
183        funcs: toplevel,
184        vars: toplevel
185    };
186}
187
188Compressor.prototype = new TreeTransformer(function(node, descend, in_list) {
189    if (node._squeezed) return node;
190    var is_scope = node instanceof AST_Scope;
191    if (is_scope) {
192        if (this.option("arrows") && is_arrow(node) && node.value) {
193            node.body = [ node.first_statement() ];
194            node.value = null;
195        }
196        node.hoist_properties(this);
197        node.hoist_declarations(this);
198        node.process_returns(this);
199    }
200    // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
201    // would call AST_Node.transform() if a different instance of AST_Node is
202    // produced after OPT().
203    // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
204    // Migrate and defer all children's AST_Node.transform() to below, which
205    // will now happen after this parent AST_Node has been properly substituted
206    // thus gives a consistent AST snapshot.
207    descend(node, this);
208    // Existing code relies on how AST_Node.optimize() worked, and omitting the
209    // following replacement call would result in degraded efficiency of both
210    // output and performance.
211    descend(node, this);
212    var opt = node.optimize(this);
213    if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
214        opt.drop_unused(this);
215        if (opt.merge_variables(this)) opt.drop_unused(this);
216        descend(opt, this);
217    }
218    if (opt === node) opt._squeezed = true;
219    return opt;
220});
221Compressor.prototype.option = function(key) {
222    return this.options[key];
223};
224Compressor.prototype.exposed = function(def) {
225    if (def.exported) return true;
226    if (def.undeclared) return true;
227    if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
228    var toplevel = this.toplevel;
229    return !all(def.orig, function(sym) {
230        return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
231    });
232};
233Compressor.prototype.compress = function(node) {
234    node = node.resolve_defines(this);
235    node.hoist_exports(this);
236    if (this.option("expression")) node.process_expression(true);
237    var merge_vars = this.options.merge_vars;
238    var passes = +this.options.passes || 1;
239    var min_count = 1 / 0;
240    var stopping = false;
241    var mangle = { ie: this.option("ie") };
242    for (var pass = 0; pass < passes; pass++) {
243        node.figure_out_scope(mangle);
244        if (pass > 0 || this.option("reduce_vars"))
245            node.reset_opt_flags(this);
246        this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
247        node = node.transform(this);
248        if (passes > 1) {
249            var count = 0;
250            node.walk(new TreeWalker(function() {
251                count++;
252            }));
253            AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
254                pass: pass,
255                min_count: min_count,
256                count: count,
257            });
258            if (count < min_count) {
259                min_count = count;
260                stopping = false;
261            } else if (stopping) {
262                break;
263            } else {
264                stopping = true;
265            }
266        }
267    }
268    if (this.option("expression")) node.process_expression(false);
269    return node;
270};
271
272(function(OPT) {
273    OPT(AST_Node, function(self, compressor) {
274        return self;
275    });
276
277    AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) {
278        if (!compressor.option("hoist_exports")) return;
279        var body = this.body, props = [];
280        for (var i = 0; i < body.length; i++) {
281            var stat = body[i];
282            if (stat instanceof AST_ExportDeclaration) {
283                body[i] = stat = stat.body;
284                if (stat instanceof AST_Definitions) {
285                    stat.definitions.forEach(function(defn) {
286                        defn.name.match_symbol(export_symbol, true);
287                    });
288                } else {
289                    export_symbol(stat.name);
290                }
291            } else if (stat instanceof AST_ExportReferences) {
292                body.splice(i--, 1);
293                [].push.apply(props, stat.properties);
294            }
295        }
296        if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props }));
297
298        function export_symbol(sym) {
299            if (!(sym instanceof AST_SymbolDeclaration)) return;
300            var node = make_node(AST_SymbolExport, sym);
301            node.alias = make_node(AST_String, node, { value: node.name });
302            props.push(node);
303        }
304    });
305
306    AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
307        var self = this;
308        var tt = new TreeTransformer(function(node) {
309            if (insert) {
310                if (node instanceof AST_Directive) node = make_node(AST_SimpleStatement, node, {
311                    body: make_node(AST_String, node),
312                });
313                if (node instanceof AST_SimpleStatement) {
314                    return transform ? transform(node) : make_node(AST_Return, node, { value: node.body });
315                }
316            } else if (node instanceof AST_Return) {
317                if (transform) return transform(node);
318                var value = node.value;
319                if (value instanceof AST_String) return make_node(AST_Directive, value);
320                return make_node(AST_SimpleStatement, node, {
321                    body: value || make_node(AST_UnaryPrefix, node, {
322                        operator: "void",
323                        expression: make_node(AST_Number, node, { value: 0 }),
324                    }),
325                });
326            }
327            if (node instanceof AST_Block) {
328                if (node instanceof AST_Lambda) {
329                    if (node !== self) return node;
330                } else if (insert === "awaits" && node instanceof AST_Try) {
331                    if (node.bfinally) return node;
332                }
333                for (var index = node.body.length; --index >= 0;) {
334                    var stat = node.body[index];
335                    if (!is_declaration(stat, true)) {
336                        node.body[index] = stat.transform(tt);
337                        break;
338                    }
339                }
340            } else if (node instanceof AST_If) {
341                node.body = node.body.transform(tt);
342                if (node.alternative) node.alternative = node.alternative.transform(tt);
343            } else if (node instanceof AST_With) {
344                node.body = node.body.transform(tt);
345            }
346            return node;
347        });
348        self.transform(tt);
349    });
350    AST_Toplevel.DEFMETHOD("unwrap_expression", function() {
351        var self = this;
352        switch (self.body.length) {
353          case 0:
354            return make_node(AST_UnaryPrefix, self, {
355                operator: "void",
356                expression: make_node(AST_Number, self, { value: 0 }),
357            });
358          case 1:
359            var stat = self.body[0];
360            if (stat instanceof AST_Directive) return make_node(AST_String, stat);
361            if (stat instanceof AST_SimpleStatement) return stat.body;
362          default:
363            return make_node(AST_Call, self, {
364                expression: make_node(AST_Function, self, {
365                    argnames: [],
366                    body: self.body,
367                }).init_vars(self),
368                args: [],
369            });
370        }
371    });
372    AST_Node.DEFMETHOD("wrap_expression", function() {
373        var self = this;
374        if (!is_statement(self)) self = make_node(AST_SimpleStatement, self, { body: self });
375        if (!(self instanceof AST_Toplevel)) self = make_node(AST_Toplevel, self, { body: [ self ] });
376        return self;
377    });
378
379    function read_property(obj, node) {
380        var key = node.get_property();
381        if (key instanceof AST_Node) return;
382        var value;
383        if (obj instanceof AST_Array) {
384            var elements = obj.elements;
385            if (key == "length") return make_node_from_constant(elements.length, obj);
386            if (typeof key == "number" && key in elements) value = elements[key];
387        } else if (obj instanceof AST_Lambda) {
388            if (key == "length") {
389                obj.length_read = true;
390                return make_node_from_constant(obj.argnames.length, obj);
391            }
392        } else if (obj instanceof AST_Object) {
393            key = "" + key;
394            var props = obj.properties;
395            for (var i = props.length; --i >= 0;) {
396                var prop = props[i];
397                if (!can_hoist_property(prop)) return;
398                if (!value && props[i].key === key) value = props[i].value;
399            }
400        }
401        return value instanceof AST_SymbolRef && value.fixed_value() || value;
402    }
403
404    function is_read_only_fn(value, name) {
405        if (value instanceof AST_Boolean) return native_fns.Boolean[name];
406        if (value instanceof AST_Number) return native_fns.Number[name];
407        if (value instanceof AST_String) return native_fns.String[name];
408        if (name == "valueOf") return false;
409        if (value instanceof AST_Array) return native_fns.Array[name];
410        if (value instanceof AST_Lambda) return native_fns.Function[name];
411        if (value instanceof AST_Object) return native_fns.Object[name];
412        if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
413    }
414
415    function is_modified(compressor, tw, node, value, level, immutable, recursive) {
416        var parent = tw.parent(level);
417        if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
418            return;
419        }
420        var lhs = is_lhs(node, parent);
421        if (lhs) return lhs;
422        if (level == 0 && value && value.is_constant()) return;
423        if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
424        if (parent instanceof AST_Assign) switch (parent.operator) {
425          case "=":
426            return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
427          case "&&=":
428          case "||=":
429          case "??=":
430            return is_modified(compressor, tw, parent, parent, level + 1);
431          default:
432            return;
433        }
434        if (parent instanceof AST_Binary) {
435            if (!lazy_op[parent.operator]) return;
436            return is_modified(compressor, tw, parent, parent, level + 1);
437        }
438        if (parent instanceof AST_Call) {
439            return !immutable
440                && parent.expression === node
441                && !parent.is_expr_pure(compressor)
442                && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
443        }
444        if (parent instanceof AST_Conditional) {
445            if (parent.condition === node) return;
446            return is_modified(compressor, tw, parent, parent, level + 1);
447        }
448        if (parent instanceof AST_ForEnumeration) return parent.init === node;
449        if (parent instanceof AST_ObjectKeyVal) {
450            if (parent.value !== node) return;
451            var obj = tw.parent(level + 1);
452            return is_modified(compressor, tw, obj, obj, level + 2);
453        }
454        if (parent instanceof AST_PropAccess) {
455            if (parent.expression !== node) return;
456            var prop = read_property(value, parent);
457            return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
458        }
459        if (parent instanceof AST_Sequence) {
460            if (parent.tail_node() !== node) return;
461            return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
462        }
463    }
464
465    function is_lambda(node) {
466        return node instanceof AST_Class || node instanceof AST_Lambda;
467    }
468
469    function safe_for_extends(node) {
470        return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function;
471    }
472
473    function is_arguments(def) {
474        return def.name == "arguments" && def.scope.uses_arguments;
475    }
476
477    function cross_scope(def, sym) {
478        do {
479            if (def === sym) return false;
480            if (sym instanceof AST_Scope) return true;
481        } while (sym = sym.parent_scope);
482    }
483
484    function can_drop_symbol(ref, compressor, keep_lambda) {
485        var def = ref.redef || ref.definition();
486        if (ref.in_arg && is_funarg(def)) return false;
487        return all(def.orig, function(sym) {
488            if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) {
489                if (sym instanceof AST_SymbolImport) return true;
490                return compressor && can_varify(compressor, sym);
491            }
492            return !(keep_lambda && sym instanceof AST_SymbolLambda);
493        });
494    }
495
496    function has_escaped(d, scope, node, parent) {
497        if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
498        if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
499        if (parent instanceof AST_ClassField) return parent.value === node && !parent.static;
500        if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve();
501        if (parent instanceof AST_VarDef) return parent.value === node;
502    }
503
504    function make_ref(ref, fixed) {
505        var node = make_node(AST_SymbolRef, ref);
506        node.fixed = fixed || make_node(AST_Undefined, ref);
507        return node;
508    }
509
510    function replace_ref(resolve, fixed) {
511        return function(node) {
512            var ref = resolve(node);
513            var node = make_ref(ref, fixed);
514            var def = ref.definition();
515            def.references.push(node);
516            def.replaced++;
517            return node;
518        };
519    }
520
521    var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
522    (function(def) {
523        def(AST_Node, noop);
524
525        function reset_def(tw, compressor, def) {
526            def.assignments = 0;
527            def.bool_return = 0;
528            def.drop_return = 0;
529            def.cross_loop = false;
530            def.direct_access = false;
531            def.escaped = [];
532            def.fixed = !def.const_redefs
533                && !def.scope.pinned()
534                && !compressor.exposed(def)
535                && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope)
536                && def.init;
537            def.reassigned = 0;
538            def.recursive_refs = 0;
539            def.references = [];
540            def.single_use = undefined;
541        }
542
543        function reset_block_variables(tw, compressor, scope) {
544            scope.variables.each(function(def) {
545                reset_def(tw, compressor, def);
546            });
547        }
548
549        function reset_variables(tw, compressor, scope) {
550            scope.fn_defs = [];
551            scope.variables.each(function(def) {
552                reset_def(tw, compressor, def);
553                var init = def.init;
554                if (init instanceof AST_LambdaDefinition) {
555                    scope.fn_defs.push(init);
556                    init.safe_ids = null;
557                }
558                if (def.fixed === null) {
559                    def.safe_ids = tw.safe_ids;
560                    mark(tw, def);
561                } else if (def.fixed) {
562                    tw.loop_ids[def.id] = tw.in_loop;
563                    mark(tw, def);
564                }
565            });
566            scope.may_call_this = function() {
567                scope.may_call_this = scope.contains_this() ? return_true : return_false;
568            };
569            if (scope.uses_arguments) scope.each_argname(function(node) {
570                node.definition().last_ref = false;
571            });
572            if (compressor.option("ie")) scope.variables.each(function(def) {
573                var d = def.orig[0].definition();
574                if (d !== def) d.fixed = false;
575            });
576        }
577
578        function safe_to_visit(tw, fn) {
579            var marker = fn.safe_ids;
580            return marker === undefined || marker === tw.safe_ids;
581        }
582
583        function walk_fn_def(tw, fn) {
584            var was_scanning = tw.fn_scanning;
585            tw.fn_scanning = fn;
586            fn.walk(tw);
587            tw.fn_scanning = was_scanning;
588        }
589
590        function revisit_fn_def(tw, fn) {
591            fn.enclosed.forEach(function(d) {
592                if (fn.variables.get(d.name) === d) return;
593                if (safe_to_read(tw, d)) return;
594                d.single_use = false;
595                var fixed = d.fixed;
596                if (typeof fixed == "function") fixed = fixed();
597                if (fixed instanceof AST_Lambda && fixed.safe_ids !== undefined) return;
598                d.fixed = false;
599            });
600        }
601
602        function mark_fn_def(tw, def, fn) {
603            var marker = fn.safe_ids;
604            if (marker === undefined) return;
605            if (marker === false) return;
606            if (fn.parent_scope.resolve().may_call_this === return_true) {
607                if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn);
608            } else if (marker) {
609                var visited = member(fn, tw.fn_visited);
610                if (marker === tw.safe_ids) {
611                    if (!visited) walk_fn_def(tw, fn);
612                } else if (visited) {
613                    revisit_fn_def(tw, fn);
614                } else {
615                    fn.safe_ids = false;
616                }
617            } else if (tw.fn_scanning && tw.fn_scanning !== def.scope.resolve()) {
618                fn.safe_ids = false;
619            } else {
620                fn.safe_ids = tw.safe_ids;
621                walk_fn_def(tw, fn);
622            }
623        }
624
625        function pop_scope(tw, scope) {
626            var fn_defs = scope.fn_defs;
627            var tangled = scope.may_call_this === return_true ? fn_defs : fn_defs.filter(function(fn) {
628                if (fn.safe_ids === false) return true;
629                fn.safe_ids = tw.safe_ids;
630                walk_fn_def(tw, fn);
631                return false;
632            });
633            pop(tw);
634            tangled.forEach(function(fn) {
635                fn.safe_ids = tw.safe_ids;
636                walk_fn_def(tw, fn);
637            });
638            fn_defs.forEach(function(fn) {
639                fn.safe_ids = undefined;
640            });
641            scope.fn_defs = undefined;
642            scope.may_call_this = undefined;
643        }
644
645        function push(tw, sequential) {
646            var safe_ids = Object.create(tw.safe_ids);
647            if (!sequential) safe_ids.seq = {};
648            tw.safe_ids = safe_ids;
649        }
650
651        function pop(tw) {
652            tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
653        }
654
655        function mark(tw, def) {
656            tw.safe_ids[def.id] = {};
657        }
658
659        function push_ref(def, ref) {
660            def.references.push(ref);
661            if (def.last_ref !== false) def.last_ref = ref;
662        }
663
664        function safe_to_read(tw, def) {
665            if (def.single_use == "m") return false;
666            var safe = tw.safe_ids[def.id];
667            if (safe) {
668                var in_order = HOP(tw.safe_ids, def.id);
669                if (!in_order) {
670                    var seq = tw.safe_ids.seq;
671                    if (!safe.read) {
672                        safe.read = seq;
673                    } else if (safe.read !== seq) {
674                        safe.read = true;
675                    }
676                }
677                if (def.fixed == null) {
678                    if (is_arguments(def)) return false;
679                    if (def.global && def.name == "arguments") return false;
680                    tw.loop_ids[def.id] = null;
681                    def.fixed = make_node(AST_Undefined, def.orig[0]);
682                    if (in_order) def.safe_ids = undefined;
683                    return true;
684                }
685                return !safe.assign || safe.assign === tw.safe_ids;
686            }
687            return def.fixed instanceof AST_LambdaDefinition;
688        }
689
690        function safe_to_assign(tw, def, declare) {
691            if (!declare) {
692                if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false;
693                if (!all(def.orig, function(sym) {
694                    return !(sym instanceof AST_SymbolConst);
695                })) return false;
696            }
697            if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
698                return !(sym instanceof AST_SymbolLet);
699            });
700            if (def.fixed === false || def.fixed === 0) return false;
701            var safe = tw.safe_ids[def.id];
702            if (def.safe_ids) {
703                def.safe_ids[def.id] = false;
704                def.safe_ids = undefined;
705                return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read;
706            }
707            if (!HOP(tw.safe_ids, def.id)) {
708                if (!safe) return false;
709                if (safe.read || tw.in_loop) {
710                    var scope = tw.find_parent(AST_BlockScope);
711                    if (scope instanceof AST_Class) return false;
712                    if (def.scope.resolve() !== scope.resolve()) return false;
713                }
714                safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
715            }
716            if (def.fixed != null && safe.read) {
717                if (safe.read !== tw.safe_ids.seq) return false;
718                if (tw.loop_ids[def.id] !== tw.in_loop) return false;
719            }
720            return safe_to_read(tw, def) && all(def.orig, function(sym) {
721                return !(sym instanceof AST_SymbolLambda);
722            });
723        }
724
725        function ref_once(compressor, def) {
726            return compressor.option("unused")
727                && !def.scope.pinned()
728                && def.single_use !== false
729                && def.references.length - def.recursive_refs == 1
730                && !(is_funarg(def) && def.scope.uses_arguments);
731        }
732
733        function is_immutable(value) {
734            if (!value) return false;
735            if (value instanceof AST_Assign) {
736                var op = value.operator;
737                return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)];
738            }
739            if (value instanceof AST_Sequence) return is_immutable(value.tail_node());
740            return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
741        }
742
743        function value_in_use(node, parent) {
744            if (parent instanceof AST_Array) return true;
745            if (parent instanceof AST_Binary) return lazy_op[parent.operator];
746            if (parent instanceof AST_Conditional) return parent.condition !== node;
747            if (parent instanceof AST_Sequence) return parent.tail_node() === node;
748            if (parent instanceof AST_Spread) return true;
749        }
750
751        function mark_escaped(tw, d, scope, node, value, level, depth) {
752            var parent = tw.parent(level);
753            if (value && value.is_constant()) return;
754            if (has_escaped(d, scope, node, parent)) {
755                d.escaped.push(parent);
756                if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
757                if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
758                if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true;
759                if (d.fixed) d.fixed.escaped = d.escaped;
760                return;
761            } else if (value_in_use(node, parent)) {
762                mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
763            } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
764                var obj = tw.parent(level + 1);
765                mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
766            } else if (parent instanceof AST_PropAccess && parent.expression === node) {
767                value = read_property(value, parent);
768                mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
769                if (value) return;
770            }
771            if (level > 0) return;
772            if (parent instanceof AST_Call && parent.expression === node) return;
773            if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
774            if (parent instanceof AST_SimpleStatement) return;
775            if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
776            d.direct_access = true;
777            if (d.fixed) d.fixed.direct_access = true;
778        }
779
780        function mark_assignment_to_arguments(node) {
781            if (!(node instanceof AST_Sub)) return;
782            var expr = node.expression;
783            if (!(expr instanceof AST_SymbolRef)) return;
784            var def = expr.definition();
785            if (!is_arguments(def)) return;
786            var key = node.property;
787            if (key.is_constant()) key = key.value;
788            if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return;
789            def.reassigned++;
790            (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
791                if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
792            });
793        }
794
795        function make_fixed(save, fn) {
796            var prev_save, prev_value;
797            return function() {
798                var current = save();
799                if (prev_save !== current) {
800                    prev_save = current;
801                    prev_value = fn(current);
802                }
803                return prev_value;
804            };
805        }
806
807        function make_fixed_default(compressor, node, save) {
808            var prev_save, prev_seq;
809            return function() {
810                if (prev_seq === node) return node;
811                var current = save();
812                var ev = fuzzy_eval(compressor, current, true);
813                if (ev instanceof AST_Node) {
814                    prev_seq = node;
815                } else if (prev_save !== current) {
816                    prev_save = current;
817                    prev_seq = ev === undefined ? make_sequence(node, [ current, node.value ]) : current;
818                }
819                return prev_seq;
820            };
821        }
822
823        function scan_declaration(tw, compressor, lhs, fixed, visit) {
824            var scanner = new TreeWalker(function(node) {
825                if (node instanceof AST_DefaultValue) {
826                    reset_flags(node);
827                    push(tw, true);
828                    node.value.walk(tw);
829                    pop(tw);
830                    var save = fixed;
831                    if (save) fixed = make_fixed_default(compressor, node, save);
832                    node.name.walk(scanner);
833                    fixed = save;
834                    return true;
835                }
836                if (node instanceof AST_DestructuredArray) {
837                    reset_flags(node);
838                    var save = fixed;
839                    node.elements.forEach(function(node, index) {
840                        if (node instanceof AST_Hole) return reset_flags(node);
841                        if (save) fixed = make_fixed(save, function(value) {
842                            return make_node(AST_Sub, node, {
843                                expression: value,
844                                property: make_node(AST_Number, node, { value: index }),
845                            });
846                        });
847                        node.walk(scanner);
848                    });
849                    if (node.rest) {
850                        var fixed_node;
851                        if (save) fixed = compressor.option("rests") && make_fixed(save, function(value) {
852                            if (!(value instanceof AST_Array)) return node;
853                            for (var i = 0, len = node.elements.length; i < len; i++) {
854                                if (value.elements[i] instanceof AST_Spread) return node;
855                            }
856                            if (!fixed_node) fixed_node = make_node(AST_Array, node, {});
857                            fixed_node.elements = value.elements.slice(len);
858                            return fixed_node;
859                        });
860                        node.rest.walk(scanner);
861                    }
862                    fixed = save;
863                    return true;
864                }
865                if (node instanceof AST_DestructuredObject) {
866                    reset_flags(node);
867                    var save = fixed;
868                    node.properties.forEach(function(node) {
869                        reset_flags(node);
870                        if (node.key instanceof AST_Node) {
871                            push(tw);
872                            node.key.walk(tw);
873                            pop(tw);
874                        }
875                        if (save) fixed = make_fixed(save, function(value) {
876                            var key = node.key;
877                            var type = AST_Sub;
878                            if (typeof key == "string") {
879                                if (is_identifier_string(key)) {
880                                    type = AST_Dot;
881                                } else {
882                                    key = make_node_from_constant(key, node);
883                                }
884                            }
885                            return make_node(type, node, {
886                                expression: value,
887                                property: key,
888                            });
889                        });
890                        node.value.walk(scanner);
891                    });
892                    if (node.rest) {
893                        fixed = false;
894                        node.rest.walk(scanner);
895                    }
896                    fixed = save;
897                    return true;
898                }
899                visit(node, fixed, function() {
900                    var save_len = tw.stack.length;
901                    for (var i = 0, len = scanner.stack.length - 1; i < len; i++) {
902                        tw.stack.push(scanner.stack[i]);
903                    }
904                    node.walk(tw);
905                    tw.stack.length = save_len;
906                });
907                return true;
908            });
909            lhs.walk(scanner);
910        }
911
912        function reduce_iife(tw, descend, compressor) {
913            var fn = this;
914            fn.inlined = false;
915            var iife = tw.parent();
916            var sequential = !is_async(fn) && !is_generator(fn);
917            var hit = !sequential;
918            var aborts = false;
919            fn.walk(new TreeWalker(function(node) {
920                if (hit) return aborts = true;
921                if (node instanceof AST_Return) return hit = true;
922                if (node instanceof AST_Scope && node !== fn) return true;
923            }));
924            if (aborts) push(tw, sequential);
925            reset_variables(tw, compressor, fn);
926            // Virtually turn IIFE parameters into variable definitions:
927            //   (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})()
928            // So existing transformation rules can work on them.
929            var safe = !fn.uses_arguments || tw.has_directive("use strict");
930            fn.argnames.forEach(function(argname, i) {
931                var value = iife.args[i];
932                scan_declaration(tw, compressor, argname, function() {
933                    var j = fn.argnames.indexOf(argname);
934                    var arg = j < 0 ? value : iife.args[j];
935                    if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0];
936                    return arg || make_node(AST_Undefined, iife);
937                }, visit);
938            });
939            var rest = fn.rest, fixed_node;
940            if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() {
941                if (fn.rest !== rest) return rest;
942                if (!fixed_node) fixed_node = make_node(AST_Array, fn, {});
943                fixed_node.elements = iife.args.slice(fn.argnames.length);
944                return fixed_node;
945            }, visit);
946            walk_lambda(fn, tw);
947            var safe_ids = tw.safe_ids;
948            pop_scope(tw, fn);
949            if (!aborts) tw.safe_ids = safe_ids;
950            return true;
951
952            function visit(node, fixed) {
953                var d = node.definition();
954                if (fixed && safe && d.fixed === undefined) {
955                    mark(tw, d);
956                    tw.loop_ids[d.id] = tw.in_loop;
957                    d.fixed = fixed;
958                    d.fixed.assigns = [ node ];
959                } else {
960                    d.fixed = false;
961                }
962            }
963        }
964
965        def(AST_Assign, function(tw, descend, compressor) {
966            var node = this;
967            var left = node.left;
968            var right = node.right;
969            var ld = left instanceof AST_SymbolRef && left.definition();
970            var scan = ld || left instanceof AST_Destructured;
971            switch (node.operator) {
972              case "=":
973                if (left.equals(right) && !left.has_side_effects(compressor)) {
974                    right.walk(tw);
975                    walk_prop(left);
976                    node.redundant = true;
977                    return true;
978                }
979                if (ld && right instanceof AST_LambdaExpression) {
980                    walk_assign();
981                    right.parent_scope.resolve().fn_defs.push(right);
982                    right.safe_ids = null;
983                    if (!ld.fixed || !node.write_only) mark_fn_def(tw, ld, right);
984                    return true;
985                }
986                if (scan) {
987                    right.walk(tw);
988                    walk_assign();
989                    return true;
990                }
991                mark_assignment_to_arguments(left);
992                return;
993              case "&&=":
994              case "||=":
995              case "??=":
996                var lazy = true;
997              default:
998                if (!scan) {
999                    mark_assignment_to_arguments(left);
1000                    return walk_lazy();
1001                }
1002                ld.assignments++;
1003                var fixed = ld.fixed;
1004                if (is_modified(compressor, tw, node, node, 0)) {
1005                    ld.fixed = false;
1006                    return walk_lazy();
1007                }
1008                var safe = safe_to_read(tw, ld);
1009                if (lazy) push(tw, true);
1010                right.walk(tw);
1011                if (lazy) pop(tw);
1012                if (safe && !left.in_arg && safe_to_assign(tw, ld)) {
1013                    push_ref(ld, left);
1014                    mark(tw, ld);
1015                    if (ld.single_use) ld.single_use = false;
1016                    left.fixed = ld.fixed = function() {
1017                        return make_node(AST_Binary, node, {
1018                            operator: node.operator.slice(0, -1),
1019                            left: make_ref(left, fixed),
1020                            right: node.right,
1021                        });
1022                    };
1023                    left.fixed.assigns = !fixed || !fixed.assigns ? [ ld.orig[0] ] : fixed.assigns.slice();
1024                    left.fixed.assigns.push(node);
1025                    left.fixed.to_binary = replace_ref(function(node) {
1026                        return node.left;
1027                    }, fixed);
1028                } else {
1029                    left.walk(tw);
1030                    ld.fixed = false;
1031                }
1032                return true;
1033            }
1034
1035            function walk_prop(lhs) {
1036                reset_flags(lhs);
1037                if (lhs instanceof AST_Dot) {
1038                    walk_prop(lhs.expression);
1039                } else if (lhs instanceof AST_Sub) {
1040                    walk_prop(lhs.expression);
1041                    lhs.property.walk(tw);
1042                } else if (lhs instanceof AST_SymbolRef) {
1043                    var d = lhs.definition();
1044                    push_ref(d, lhs);
1045                    if (d.fixed) {
1046                        lhs.fixed = d.fixed;
1047                        if (lhs.fixed.assigns) {
1048                            lhs.fixed.assigns.push(node);
1049                        } else {
1050                            lhs.fixed.assigns = [ node ];
1051                        }
1052                    }
1053                } else {
1054                    lhs.walk(tw);
1055                }
1056            }
1057
1058            function walk_assign() {
1059                var recursive = ld && recursive_ref(tw, ld);
1060                var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive);
1061                scan_declaration(tw, compressor, left, function() {
1062                    return node.right;
1063                }, function(sym, fixed, walk) {
1064                    if (!(sym instanceof AST_SymbolRef)) {
1065                        mark_assignment_to_arguments(sym);
1066                        walk();
1067                        return;
1068                    }
1069                    var d = sym.definition();
1070                    d.assignments++;
1071                    if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) {
1072                        walk();
1073                        d.fixed = false;
1074                    } else {
1075                        push_ref(d, sym);
1076                        mark(tw, d);
1077                        if (left instanceof AST_Destructured
1078                            || d.orig.length == 1 && d.orig[0] instanceof AST_SymbolDefun) {
1079                            d.single_use = false;
1080                        }
1081                        tw.loop_ids[d.id] = tw.in_loop;
1082                        d.fixed = modified ? 0 : fixed;
1083                        sym.fixed = fixed;
1084                        sym.fixed.assigns = [ node ];
1085                        mark_escaped(tw, d, sym.scope, node, right, 0, 1);
1086                    }
1087                });
1088            }
1089
1090            function walk_lazy() {
1091                if (!lazy) return;
1092                left.walk(tw);
1093                push(tw, true);
1094                right.walk(tw);
1095                pop(tw);
1096                return true;
1097            }
1098        });
1099        def(AST_Binary, function(tw) {
1100            if (!lazy_op[this.operator]) return;
1101            this.left.walk(tw);
1102            push(tw, true);
1103            this.right.walk(tw);
1104            pop(tw);
1105            return true;
1106        });
1107        def(AST_BlockScope, function(tw, descend, compressor) {
1108            reset_block_variables(tw, compressor, this);
1109        });
1110        def(AST_Call, function(tw, descend) {
1111            var node = this;
1112            var exp = node.expression;
1113            if (exp instanceof AST_LambdaExpression) {
1114                var iife = is_iife_single(node);
1115                node.args.forEach(function(arg) {
1116                    arg.walk(tw);
1117                    if (arg instanceof AST_Spread) iife = false;
1118                });
1119                if (iife) exp.reduce_vars = reduce_iife;
1120                exp.walk(tw);
1121                if (iife) delete exp.reduce_vars;
1122                return true;
1123            }
1124            if (node.TYPE == "Call") switch (tw.in_boolean_context()) {
1125              case "d":
1126                var drop = true;
1127              case true:
1128                mark_refs(exp, drop);
1129            }
1130            exp.walk(tw);
1131            var optional = node.optional;
1132            if (optional) push(tw, true);
1133            node.args.forEach(function(arg) {
1134                arg.walk(tw);
1135            });
1136            if (optional) pop(tw);
1137            var fixed = exp instanceof AST_SymbolRef && exp.fixed_value();
1138            if (fixed instanceof AST_Lambda) {
1139                mark_fn_def(tw, exp.definition(), fixed);
1140            } else {
1141                tw.find_parent(AST_Scope).may_call_this();
1142            }
1143            return true;
1144
1145            function mark_refs(node, drop) {
1146                if (node instanceof AST_Assign) {
1147                    if (node.operator != "=") return;
1148                    mark_refs(node.left, drop);
1149                    mark_refs(node.right, drop);
1150                } else if (node instanceof AST_Binary) {
1151                    if (!lazy_op[node.operator]) return;
1152                    mark_refs(node.left, drop);
1153                    mark_refs(node.right, drop);
1154                } else if (node instanceof AST_Conditional) {
1155                    mark_refs(node.consequent, drop);
1156                    mark_refs(node.alternative, drop);
1157                } else if (node instanceof AST_SymbolRef) {
1158                    var def = node.definition();
1159                    def.bool_return++;
1160                    if (drop) def.drop_return++;
1161                }
1162            }
1163        });
1164        def(AST_Class, function(tw, descend, compressor) {
1165            var node = this;
1166            reset_block_variables(tw, compressor, node);
1167            if (node.extends) node.extends.walk(tw);
1168            var props = node.properties.filter(function(prop) {
1169                reset_flags(prop);
1170                if (prop.key instanceof AST_Node) {
1171                    tw.push(prop);
1172                    prop.key.walk(tw);
1173                    tw.pop();
1174                }
1175                return prop.value;
1176            });
1177            if (node.name) {
1178                var d = node.name.definition();
1179                var parent = tw.parent();
1180                if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false;
1181                if (safe_to_assign(tw, d, true)) {
1182                    mark(tw, d);
1183                    tw.loop_ids[d.id] = tw.in_loop;
1184                    d.fixed = function() {
1185                        return node;
1186                    };
1187                    d.fixed.assigns = [ node ];
1188                    if (!is_safe_lexical(d)) d.single_use = false;
1189                } else {
1190                    d.fixed = false;
1191                }
1192            }
1193            props.forEach(function(prop) {
1194                tw.push(prop);
1195                if (!prop.static || is_static_field_or_init(prop) && prop.value.contains_this()) {
1196                    push(tw);
1197                    prop.value.walk(tw);
1198                    pop(tw);
1199                } else {
1200                    prop.value.walk(tw);
1201                }
1202                tw.pop();
1203            });
1204            return true;
1205        });
1206        def(AST_ClassInitBlock, function(tw, descend, compressor) {
1207            var node = this;
1208            push(tw, true);
1209            reset_variables(tw, compressor, node);
1210            descend();
1211            pop_scope(tw, node);
1212            return true;
1213        });
1214        def(AST_Conditional, function(tw) {
1215            this.condition.walk(tw);
1216            push(tw, true);
1217            this.consequent.walk(tw);
1218            pop(tw);
1219            push(tw, true);
1220            this.alternative.walk(tw);
1221            pop(tw);
1222            return true;
1223        });
1224        def(AST_DefaultValue, function(tw) {
1225            push(tw, true);
1226            this.value.walk(tw);
1227            pop(tw);
1228            this.name.walk(tw);
1229            return true;
1230        });
1231        def(AST_Do, function(tw) {
1232            var save_loop = tw.in_loop;
1233            tw.in_loop = this;
1234            push(tw);
1235            this.body.walk(tw);
1236            if (has_loop_control(this, tw.parent())) {
1237                pop(tw);
1238                push(tw);
1239            }
1240            this.condition.walk(tw);
1241            pop(tw);
1242            tw.in_loop = save_loop;
1243            return true;
1244        });
1245        def(AST_For, function(tw, descend, compressor) {
1246            var node = this;
1247            reset_block_variables(tw, compressor, node);
1248            if (node.init) node.init.walk(tw);
1249            var save_loop = tw.in_loop;
1250            tw.in_loop = node;
1251            push(tw);
1252            if (node.condition) node.condition.walk(tw);
1253            node.body.walk(tw);
1254            if (node.step) {
1255                if (has_loop_control(node, tw.parent())) {
1256                    pop(tw);
1257                    push(tw);
1258                }
1259                node.step.walk(tw);
1260            }
1261            pop(tw);
1262            tw.in_loop = save_loop;
1263            return true;
1264        });
1265        def(AST_ForEnumeration, function(tw, descend, compressor) {
1266            var node = this;
1267            reset_block_variables(tw, compressor, node);
1268            node.object.walk(tw);
1269            var save_loop = tw.in_loop;
1270            tw.in_loop = node;
1271            push(tw);
1272            var init = node.init;
1273            if (init instanceof AST_Definitions) {
1274                init.definitions[0].name.mark_symbol(function(node) {
1275                    if (node instanceof AST_SymbolDeclaration) {
1276                        var def = node.definition();
1277                        def.assignments++;
1278                        def.fixed = false;
1279                    }
1280                }, tw);
1281            } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) {
1282                init.mark_symbol(function(node) {
1283                    if (node instanceof AST_SymbolRef) {
1284                        var def = node.definition();
1285                        push_ref(def, node);
1286                        def.assignments++;
1287                        if (!node.is_immutable()) def.fixed = false;
1288                    }
1289                }, tw);
1290            } else {
1291                init.walk(tw);
1292            }
1293            node.body.walk(tw);
1294            pop(tw);
1295            tw.in_loop = save_loop;
1296            return true;
1297        });
1298        def(AST_If, function(tw) {
1299            this.condition.walk(tw);
1300            push(tw, true);
1301            this.body.walk(tw);
1302            pop(tw);
1303            if (this.alternative) {
1304                push(tw, true);
1305                this.alternative.walk(tw);
1306                pop(tw);
1307            }
1308            return true;
1309        });
1310        def(AST_LabeledStatement, function(tw) {
1311            push(tw, true);
1312            this.body.walk(tw);
1313            pop(tw);
1314            return true;
1315        });
1316        def(AST_Lambda, function(tw, descend, compressor) {
1317            var fn = this;
1318            if (!safe_to_visit(tw, fn)) return true;
1319            if (!push_uniq(tw.fn_visited, fn)) return true;
1320            fn.inlined = false;
1321            push(tw);
1322            reset_variables(tw, compressor, fn);
1323            descend();
1324            pop_scope(tw, fn);
1325            if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
1326            return true;
1327        });
1328        def(AST_LambdaDefinition, function(tw, descend, compressor) {
1329            var fn = this;
1330            var def = fn.name.definition();
1331            var parent = tw.parent();
1332            if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false;
1333            if (!safe_to_visit(tw, fn)) return true;
1334            if (!push_uniq(tw.fn_visited, fn)) return true;
1335            fn.inlined = false;
1336            push(tw);
1337            reset_variables(tw, compressor, fn);
1338            descend();
1339            pop_scope(tw, fn);
1340            return true;
1341        });
1342        def(AST_Sub, function(tw) {
1343            if (!this.optional) return;
1344            this.expression.walk(tw);
1345            push(tw, true);
1346            this.property.walk(tw);
1347            pop(tw);
1348            return true;
1349        });
1350        def(AST_Switch, function(tw, descend, compressor) {
1351            var node = this;
1352            reset_block_variables(tw, compressor, node);
1353            node.expression.walk(tw);
1354            var first = true;
1355            node.body.forEach(function(branch) {
1356                if (branch instanceof AST_Default) return;
1357                branch.expression.walk(tw);
1358                if (first) {
1359                    first = false;
1360                    push(tw, true);
1361                }
1362            })
1363            if (!first) pop(tw);
1364            walk_body(node, tw);
1365            return true;
1366        });
1367        def(AST_SwitchBranch, function(tw) {
1368            push(tw, true);
1369            walk_body(this, tw);
1370            pop(tw);
1371            return true;
1372        });
1373        def(AST_SymbolCatch, function() {
1374            this.definition().fixed = false;
1375        });
1376        def(AST_SymbolImport, function() {
1377            this.definition().fixed = false;
1378        });
1379        def(AST_SymbolRef, function(tw, descend, compressor) {
1380            var ref = this;
1381            var d = ref.definition();
1382            var fixed = d.fixed || d.last_ref && d.last_ref.fixed;
1383            push_ref(d, ref);
1384            if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) {
1385                tw.loop_ids[d.id] = tw.in_loop;
1386            }
1387            var recursive = recursive_ref(tw, d);
1388            if (recursive) recursive.enclosed.forEach(function(def) {
1389                if (d === def) return;
1390                if (def.scope.resolve() === recursive) return;
1391                var assigns = def.fixed && def.fixed.assigns;
1392                if (!assigns) return;
1393                if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
1394                var safe = tw.safe_ids[def.id];
1395                if (!safe) return;
1396                safe.assign = true;
1397            });
1398            if (d.single_use == "m" && d.fixed) {
1399                d.fixed = 0;
1400                d.single_use = false;
1401            }
1402            switch (d.fixed) {
1403              case 0:
1404                if (!safe_to_read(tw, d)) d.fixed = false;
1405              case false:
1406                var redef = d.redefined();
1407                if (redef && cross_scope(d.scope, ref.scope)) redef.single_use = false;
1408                break;
1409              case undefined:
1410                d.fixed = false;
1411                break;
1412              default:
1413                if (!safe_to_read(tw, d)) {
1414                    d.fixed = false;
1415                    break;
1416                }
1417                if (ref.in_arg && d.orig[0] instanceof AST_SymbolLambda) ref.fixed = d.scope;
1418                var value = ref.fixed_value();
1419                if (recursive) {
1420                    d.recursive_refs++;
1421                } else if (value && ref_once(compressor, d)) {
1422                    d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
1423                    d.single_use = is_lambda(value)
1424                            && !value.pinned()
1425                            && (!d.in_loop || tw.parent() instanceof AST_Call)
1426                        || !d.in_loop
1427                            && d.scope === ref.scope.resolve()
1428                            && value.is_constant_expression();
1429                } else {
1430                    d.single_use = false;
1431                }
1432                if (is_modified(compressor, tw, ref, value, 0, is_immutable(value), recursive)) {
1433                    if (d.single_use) {
1434                        d.single_use = "m";
1435                    } else {
1436                        d.fixed = 0;
1437                    }
1438                }
1439                if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true;
1440                mark_escaped(tw, d, ref.scope, ref, value, 0, 1);
1441                break;
1442            }
1443            if (!ref.fixed) ref.fixed = d.fixed === 0 ? fixed : d.fixed;
1444            var parent;
1445            if (value instanceof AST_Lambda
1446                && !((parent = tw.parent()) instanceof AST_Call && parent.expression === ref)) {
1447                mark_fn_def(tw, d, value);
1448            }
1449        });
1450        def(AST_Template, function(tw, descend) {
1451            var node = this;
1452            var tag = node.tag;
1453            if (!tag) return;
1454            if (tag instanceof AST_LambdaExpression) {
1455                node.expressions.forEach(function(exp) {
1456                    exp.walk(tw);
1457                });
1458                tag.walk(tw);
1459                return true;
1460            }
1461            tag.walk(tw);
1462            node.expressions.forEach(function(exp) {
1463                exp.walk(tw);
1464            });
1465            var fixed = tag instanceof AST_SymbolRef && tag.fixed_value();
1466            if (fixed instanceof AST_Lambda) {
1467                mark_fn_def(tw, tag.definition(), fixed);
1468            } else {
1469                tw.find_parent(AST_Scope).may_call_this();
1470            }
1471            return true;
1472        });
1473        def(AST_Toplevel, function(tw, descend, compressor) {
1474            var node = this;
1475            node.globals.each(function(def) {
1476                reset_def(tw, compressor, def);
1477            });
1478            push(tw, true);
1479            reset_variables(tw, compressor, node);
1480            descend();
1481            pop_scope(tw, node);
1482            return true;
1483        });
1484        def(AST_Try, function(tw, descend, compressor) {
1485            var node = this;
1486            reset_block_variables(tw, compressor, node);
1487            push(tw, true);
1488            walk_body(node, tw);
1489            pop(tw);
1490            if (node.bcatch) {
1491                push(tw, true);
1492                node.bcatch.walk(tw);
1493                pop(tw);
1494            }
1495            if (node.bfinally) node.bfinally.walk(tw);
1496            return true;
1497        });
1498        def(AST_Unary, function(tw, descend) {
1499            var node = this;
1500            if (!UNARY_POSTFIX[node.operator]) return;
1501            var exp = node.expression;
1502            if (!(exp instanceof AST_SymbolRef)) {
1503                mark_assignment_to_arguments(exp);
1504                return;
1505            }
1506            var d = exp.definition();
1507            d.assignments++;
1508            var fixed = d.fixed;
1509            if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) {
1510                push_ref(d, exp);
1511                mark(tw, d);
1512                if (d.single_use) d.single_use = false;
1513                d.fixed = function() {
1514                    return make_node(AST_Binary, node, {
1515                        operator: node.operator.slice(0, -1),
1516                        left: make_node(AST_UnaryPrefix, node, {
1517                            operator: "+",
1518                            expression: make_ref(exp, fixed),
1519                        }),
1520                        right: make_node(AST_Number, node, { value: 1 }),
1521                    });
1522                };
1523                d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
1524                d.fixed.assigns.push(node);
1525                if (node instanceof AST_UnaryPrefix) {
1526                    exp.fixed = d.fixed;
1527                } else {
1528                    exp.fixed = function() {
1529                        return make_node(AST_UnaryPrefix, node, {
1530                            operator: "+",
1531                            expression: make_ref(exp, fixed),
1532                        });
1533                    };
1534                    exp.fixed.assigns = fixed && fixed.assigns;
1535                    exp.fixed.to_prefix = replace_ref(function(node) {
1536                        return node.expression;
1537                    }, d.fixed);
1538                }
1539            } else {
1540                exp.walk(tw);
1541                d.fixed = false;
1542            }
1543            return true;
1544        });
1545        def(AST_VarDef, function(tw, descend, compressor) {
1546            var node = this;
1547            var value = node.value;
1548            if (value instanceof AST_LambdaExpression && node.name instanceof AST_SymbolDeclaration) {
1549                walk_defn();
1550                value.parent_scope.resolve().fn_defs.push(value);
1551                value.safe_ids = null;
1552                var ld = node.name.definition();
1553                if (!ld.fixed) mark_fn_def(tw, ld, value);
1554            } else if (value) {
1555                value.walk(tw);
1556                walk_defn();
1557            } else if (tw.parent() instanceof AST_Let) {
1558                walk_defn();
1559            }
1560            return true;
1561
1562            function walk_defn() {
1563                scan_declaration(tw, compressor, node.name, function() {
1564                    return node.value || make_node(AST_Undefined, node);
1565                }, function(name, fixed) {
1566                    var d = name.definition();
1567                    if (fixed && safe_to_assign(tw, d, true)) {
1568                        mark(tw, d);
1569                        tw.loop_ids[d.id] = tw.in_loop;
1570                        d.fixed = fixed;
1571                        d.fixed.assigns = [ node ];
1572                        if (name instanceof AST_SymbolConst && d.redefined()
1573                            || !(can_drop_symbol(name) || is_safe_lexical(d))) {
1574                            d.single_use = false;
1575                        }
1576                    } else {
1577                        d.fixed = false;
1578                    }
1579                });
1580            }
1581        });
1582        def(AST_While, function(tw, descend) {
1583            var save_loop = tw.in_loop;
1584            tw.in_loop = this;
1585            push(tw);
1586            descend();
1587            pop(tw);
1588            tw.in_loop = save_loop;
1589            return true;
1590        });
1591    })(function(node, func) {
1592        node.DEFMETHOD("reduce_vars", func);
1593    });
1594
1595    function reset_flags(node) {
1596        node._squeezed = false;
1597        node._optimized = false;
1598        if (node instanceof AST_BlockScope) node._var_names = undefined;
1599        if (node instanceof AST_SymbolRef) node.fixed = undefined;
1600    }
1601
1602    AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
1603        var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
1604            reset_flags(node);
1605            return node.reduce_vars(tw, descend, compressor);
1606        } : reset_flags);
1607        // Flow control for visiting lambda definitions
1608        tw.fn_scanning = null;
1609        tw.fn_visited = [];
1610        // Record the loop body in which `AST_SymbolDeclaration` is first encountered
1611        tw.in_loop = null;
1612        tw.loop_ids = Object.create(null);
1613        // Stack of look-up tables to keep track of whether a `SymbolDef` has been
1614        // properly assigned before use:
1615        // - `push()` & `pop()` when visiting conditional branches
1616        // - backup & restore via `save_ids` when visiting out-of-order sections
1617        tw.safe_ids = Object.create(null);
1618        tw.safe_ids.seq = {};
1619        this.walk(tw);
1620    });
1621
1622    AST_Symbol.DEFMETHOD("fixed_value", function(ref_only) {
1623        var def = this.definition();
1624        var fixed = def.fixed;
1625        if (fixed) {
1626            if (this.fixed) fixed = this.fixed;
1627            return (fixed instanceof AST_Node ? fixed : fixed()).tail_node();
1628        }
1629        fixed = fixed === 0 && this.fixed;
1630        if (!fixed) return fixed;
1631        var value = (fixed instanceof AST_Node ? fixed : fixed()).tail_node();
1632        if (ref_only && def.escaped.depth != 1 && is_object(value, true)) return value;
1633        if (value.is_constant()) return value;
1634    });
1635
1636    AST_SymbolRef.DEFMETHOD("is_immutable", function() {
1637        var def = this.redef || this.definition();
1638        if (!(def.orig[0] instanceof AST_SymbolLambda)) return false;
1639        if (def.orig.length == 1) return true;
1640        if (!this.in_arg) return false;
1641        return !(def.orig[1] instanceof AST_SymbolFunarg);
1642    });
1643
1644    AST_Node.DEFMETHOD("convert_symbol", noop);
1645    function convert_destructured(type, process) {
1646        return this.transform(new TreeTransformer(function(node, descend) {
1647            if (node instanceof AST_DefaultValue) {
1648                node = node.clone();
1649                node.name = node.name.transform(this);
1650                return node;
1651            }
1652            if (node instanceof AST_Destructured) {
1653                node = node.clone();
1654                descend(node, this);
1655                return node;
1656            }
1657            if (node instanceof AST_DestructuredKeyVal) {
1658                node = node.clone();
1659                node.value = node.value.transform(this);
1660                return node;
1661            }
1662            return node.convert_symbol(type, process);
1663        }));
1664    }
1665    AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured);
1666    AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
1667    function convert_symbol(type, process) {
1668        var node = make_node(type, this);
1669        return process(node, this) || node;
1670    }
1671    AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
1672    AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
1673
1674    function process_to_assign(ref) {
1675        var def = ref.definition();
1676        def.assignments++;
1677        def.references.push(ref);
1678    }
1679
1680    function mark_destructured(process, tw) {
1681        var marker = new TreeWalker(function(node) {
1682            if (node instanceof AST_DefaultValue) {
1683                node.value.walk(tw);
1684                node.name.walk(marker);
1685                return true;
1686            }
1687            if (node instanceof AST_DestructuredKeyVal) {
1688                if (node.key instanceof AST_Node) node.key.walk(tw);
1689                node.value.walk(marker);
1690                return true;
1691            }
1692            return process(node);
1693        });
1694        this.walk(marker);
1695    }
1696    AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured);
1697    AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured);
1698    function mark_symbol(process) {
1699        return process(this);
1700    }
1701    AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol);
1702    AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol);
1703
1704    AST_Node.DEFMETHOD("match_symbol", function(predicate) {
1705        return predicate(this);
1706    });
1707    function match_destructured(predicate, ignore_side_effects) {
1708        var found = false;
1709        var tw = new TreeWalker(function(node) {
1710            if (found) return true;
1711            if (node instanceof AST_DefaultValue) {
1712                if (!ignore_side_effects) return found = true;
1713                node.name.walk(tw);
1714                return true;
1715            }
1716            if (node instanceof AST_DestructuredKeyVal) {
1717                if (!ignore_side_effects && node.key instanceof AST_Node) return found = true;
1718                node.value.walk(tw);
1719                return true;
1720            }
1721            if (predicate(node)) return found = true;
1722        });
1723        this.walk(tw);
1724        return found;
1725    }
1726    AST_DefaultValue.DEFMETHOD("match_symbol", match_destructured);
1727    AST_Destructured.DEFMETHOD("match_symbol", match_destructured);
1728
1729    function in_async_generator(scope) {
1730        return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction;
1731    }
1732
1733    function find_scope(compressor) {
1734        var level = 0, node = compressor.self();
1735        do {
1736            if (node.variables) return node;
1737        } while (node = compressor.parent(level++));
1738    }
1739
1740    function find_try(compressor, level, node, scope, may_throw, sync) {
1741        for (var parent; parent = compressor.parent(level++); node = parent) {
1742            if (parent === scope) return false;
1743            if (sync && parent instanceof AST_Lambda) {
1744                if (parent.name || is_async(parent) || is_generator(parent)) return true;
1745            } else if (parent instanceof AST_Try) {
1746                if (parent.bfinally && parent.bfinally !== node) return true;
1747                if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
1748            }
1749        }
1750        return false;
1751    }
1752
1753    var identifier_atom = makePredicate("Infinity NaN undefined");
1754    function is_lhs_read_only(lhs, compressor) {
1755        if (lhs instanceof AST_Atom) return true;
1756        if (lhs instanceof AST_ObjectIdentity) return true;
1757        if (lhs instanceof AST_PropAccess) {
1758            if (lhs.property === "__proto__") return true;
1759            lhs = lhs.expression;
1760            if (lhs instanceof AST_SymbolRef) {
1761                if (lhs.is_immutable()) return false;
1762                lhs = lhs.fixed_value();
1763            }
1764            if (!lhs) return true;
1765            if (lhs.tail_node().is_constant()) return true;
1766            return is_lhs_read_only(lhs, compressor);
1767        }
1768        if (lhs instanceof AST_SymbolRef) {
1769            if (lhs.is_immutable()) return true;
1770            var def = lhs.definition();
1771            return compressor.exposed(def) && identifier_atom[def.name];
1772        }
1773        return false;
1774    }
1775
1776    function make_node(ctor, orig, props) {
1777        if (props) {
1778            props.start = orig.start;
1779            props.end = orig.end;
1780        } else {
1781            props = orig;
1782        }
1783        return new ctor(props);
1784    }
1785
1786    function make_sequence(orig, expressions) {
1787        if (expressions.length == 1) return expressions[0];
1788        return make_node(AST_Sequence, orig, { expressions: expressions.reduce(merge_sequence, []) });
1789    }
1790
1791    function make_node_from_constant(val, orig) {
1792        switch (typeof val) {
1793          case "string":
1794            return make_node(AST_String, orig, { value: val });
1795          case "number":
1796            if (isNaN(val)) return make_node(AST_NaN, orig);
1797            if (isFinite(val)) {
1798                return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
1799                    operator: "-",
1800                    expression: make_node(AST_Number, orig, { value: -val }),
1801                }) : make_node(AST_Number, orig, { value: val });
1802            }
1803            return val < 0 ? make_node(AST_UnaryPrefix, orig, {
1804                operator: "-",
1805                expression: make_node(AST_Infinity, orig),
1806            }) : make_node(AST_Infinity, orig);
1807          case "boolean":
1808            return make_node(val ? AST_True : AST_False, orig);
1809          case "undefined":
1810            return make_node(AST_Undefined, orig);
1811          default:
1812            if (val === null) {
1813                return make_node(AST_Null, orig);
1814            }
1815            if (val instanceof RegExp) {
1816                return make_node(AST_RegExp, orig, { value: val });
1817            }
1818            throw new Error(string_template("Can't handle constant of type: {type}", { type: typeof val }));
1819        }
1820    }
1821
1822    function needs_unbinding(val) {
1823        return val instanceof AST_PropAccess
1824            || is_undeclared_ref(val) && val.name == "eval";
1825    }
1826
1827    // we shouldn't compress (1,func)(something) to
1828    // func(something) because that changes the meaning of
1829    // the func (becomes lexical instead of global).
1830    function maintain_this_binding(parent, orig, val) {
1831        var wrap = false;
1832        if (parent.TYPE == "Call") {
1833            wrap = parent.expression === orig && needs_unbinding(val);
1834        } else if (parent instanceof AST_Template) {
1835            wrap = parent.tag === orig && needs_unbinding(val);
1836        } else if (parent instanceof AST_UnaryPrefix) {
1837            wrap = parent.operator == "delete"
1838                || parent.operator == "typeof" && is_undeclared_ref(val);
1839        }
1840        return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
1841    }
1842
1843    function merge_expression(base, target) {
1844        var fixed_by_id = new Dictionary();
1845        base.walk(new TreeWalker(function(node) {
1846            if (!(node instanceof AST_SymbolRef)) return;
1847            var def = node.definition();
1848            var fixed = node.fixed;
1849            if (!fixed || !fixed_by_id.has(def.id)) {
1850                fixed_by_id.set(def.id, fixed);
1851            } else if (fixed_by_id.get(def.id) !== fixed) {
1852                fixed_by_id.set(def.id, false);
1853            }
1854        }));
1855        if (fixed_by_id.size() > 0) target.walk(new TreeWalker(function(node) {
1856            if (!(node instanceof AST_SymbolRef)) return;
1857            var def = node.definition();
1858            var fixed = node.fixed;
1859            if (!fixed || !fixed_by_id.has(def.id)) return;
1860            if (fixed_by_id.get(def.id) !== fixed) node.fixed = false;
1861        }));
1862        return target;
1863    }
1864
1865    function merge_sequence(array, node) {
1866        if (node instanceof AST_Sequence) {
1867            [].push.apply(array, node.expressions);
1868        } else {
1869            array.push(node);
1870        }
1871        return array;
1872    }
1873
1874    function is_lexical_definition(stat) {
1875        return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let;
1876    }
1877
1878    function safe_to_trim(stat) {
1879        if (stat instanceof AST_LambdaDefinition) {
1880            var def = stat.name.definition();
1881            var scope = stat.name.scope;
1882            return def.scope === scope || all(def.references, function(ref) {
1883                var s = ref.scope;
1884                do {
1885                    if (s === scope) return true;
1886                } while (s = s.parent_scope);
1887            });
1888        }
1889        return !is_lexical_definition(stat);
1890    }
1891
1892    function as_statement_array(thing) {
1893        if (thing === null) return [];
1894        if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ];
1895        if (thing instanceof AST_EmptyStatement) return [];
1896        if (is_statement(thing)) return [ thing ];
1897        throw new Error("Can't convert thing to statement array");
1898    }
1899
1900    function is_empty(thing) {
1901        if (thing === null) return true;
1902        if (thing instanceof AST_EmptyStatement) return true;
1903        if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
1904        return false;
1905    }
1906
1907    function has_declarations_only(block) {
1908        return all(block.body, function(stat) {
1909            return is_empty(stat)
1910                || stat instanceof AST_Defun
1911                || stat instanceof AST_Var && declarations_only(stat);
1912        });
1913    }
1914
1915    function loop_body(x) {
1916        if (x instanceof AST_IterationStatement) {
1917            return x.body instanceof AST_BlockStatement ? x.body : x;
1918        }
1919        return x;
1920    }
1921
1922    function is_iife_call(node) {
1923        if (node.TYPE != "Call") return false;
1924        do {
1925            node = node.expression;
1926        } while (node instanceof AST_PropAccess);
1927        return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
1928    }
1929
1930    function is_iife_single(call) {
1931        var exp = call.expression;
1932        if (exp.name) return false;
1933        if (!(call instanceof AST_New)) return true;
1934        var found = false;
1935        exp.walk(new TreeWalker(function(node) {
1936            if (found) return true;
1937            if (node instanceof AST_NewTarget) return found = true;
1938            if (node instanceof AST_Scope && node !== exp) return true;
1939        }));
1940        return !found;
1941    }
1942
1943    function is_undeclared_ref(node) {
1944        return node instanceof AST_SymbolRef && node.definition().undeclared;
1945    }
1946
1947    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");
1948    AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
1949        return this.defined
1950            || !this.definition().undeclared
1951            || compressor.option("unsafe") && global_names[this.name];
1952    });
1953
1954    function is_static_field_or_init(prop) {
1955        return prop.static && prop.value && (prop instanceof AST_ClassField || prop instanceof AST_ClassInit);
1956    }
1957
1958    function declarations_only(node) {
1959        return all(node.definitions, function(var_def) {
1960            return !var_def.value;
1961        });
1962    }
1963
1964    function is_declaration(stat, lexical) {
1965        if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) {
1966            if (prop.key instanceof AST_Node) return false;
1967            return !is_static_field_or_init(prop);
1968        });
1969        if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat);
1970        if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical);
1971        if (stat instanceof AST_ExportDefault) return is_declaration(stat.body, lexical);
1972        return stat instanceof AST_LambdaDefinition;
1973    }
1974
1975    function is_last_statement(body, stat) {
1976        var index = body.lastIndexOf(stat);
1977        if (index < 0) return false;
1978        while (++index < body.length) {
1979            if (!is_declaration(body[index], true)) return false;
1980        }
1981        return true;
1982    }
1983
1984    // Certain combination of unused name + side effect leads to invalid AST:
1985    //    https://github.com/mishoo/UglifyJS/issues/44
1986    //    https://github.com/mishoo/UglifyJS/issues/1838
1987    //    https://github.com/mishoo/UglifyJS/issues/3371
1988    // We fix it at this stage by moving the `var` outside the `for`.
1989    function patch_for_init(node, in_list) {
1990        var block;
1991        if (node.init instanceof AST_BlockStatement) {
1992            block = node.init;
1993            node.init = block.body.pop();
1994            block.body.push(node);
1995        }
1996        if (node.init instanceof AST_Defun) {
1997            if (!block) block = make_node(AST_BlockStatement, node, { body: [ node ] });
1998            block.body.splice(-1, 0, node.init);
1999            node.init = null;
2000        } else if (node.init instanceof AST_SimpleStatement) {
2001            node.init = node.init.body;
2002        } else if (is_empty(node.init)) {
2003            node.init = null;
2004        }
2005        if (!block) return;
2006        return in_list ? List.splice(block.body) : block;
2007    }
2008
2009    function tighten_body(statements, compressor) {
2010        var in_lambda = last_of(compressor, function(node) {
2011            return node instanceof AST_Lambda;
2012        });
2013        var block_scope, iife_in_try, in_iife_single, in_loop, in_try, scope;
2014        find_loop_scope_try();
2015        var changed, last_changed, max_iter = 10;
2016        do {
2017            last_changed = changed;
2018            changed = 0;
2019            if (eliminate_spurious_blocks(statements)) changed = 1;
2020            if (!changed && last_changed == 1) break;
2021            if (compressor.option("dead_code")) {
2022                if (eliminate_dead_code(statements, compressor)) changed = 2;
2023                if (!changed && last_changed == 2) break;
2024            }
2025            if (compressor.option("if_return")) {
2026                if (handle_if_return(statements, compressor)) changed = 3;
2027                if (!changed && last_changed == 3) break;
2028            }
2029            if (compressor.option("awaits") && compressor.option("side_effects")) {
2030                if (trim_awaits(statements, compressor)) changed = 4;
2031                if (!changed && last_changed == 4) break;
2032            }
2033            if (compressor.option("inline") >= 4) {
2034                if (inline_iife(statements, compressor)) changed = 5;
2035                if (!changed && last_changed == 5) break;
2036            }
2037            if (compressor.sequences_limit > 0) {
2038                if (sequencesize(statements, compressor)) changed = 6;
2039                if (!changed && last_changed == 6) break;
2040                if (sequencesize_2(statements, compressor)) changed = 7;
2041                if (!changed && last_changed == 7) break;
2042            }
2043            if (compressor.option("join_vars")) {
2044                if (join_consecutive_vars(statements)) changed = 8;
2045                if (!changed && last_changed == 8) break;
2046            }
2047            if (compressor.option("collapse_vars")) {
2048                if (collapse(statements, compressor)) changed = 9;
2049            }
2050        } while (changed && max_iter-- > 0);
2051        return statements;
2052
2053        function last_of(compressor, predicate) {
2054            var block = compressor.self(), level = 0, stat;
2055            do {
2056                if (block instanceof AST_Catch) {
2057                    block = compressor.parent(level++);
2058                } else if (block instanceof AST_LabeledStatement) {
2059                    block = block.body;
2060                } else if (block instanceof AST_SwitchBranch) {
2061                    var branches = compressor.parent(level);
2062                    if (branches.body[branches.body.length - 1] === block || has_break(block.body)) {
2063                        level++;
2064                        block = branches;
2065                    }
2066                }
2067                do {
2068                    stat = block;
2069                    if (predicate(stat)) return stat;
2070                    block = compressor.parent(level++);
2071                } while (block instanceof AST_If);
2072            } while (stat
2073                && (block instanceof AST_BlockStatement
2074                    || block instanceof AST_Catch
2075                    || block instanceof AST_Scope
2076                    || block instanceof AST_SwitchBranch
2077                    || block instanceof AST_Try)
2078                && is_last_statement(block.body, stat));
2079
2080            function has_break(stats) {
2081                for (var i = stats.length; --i >= 0;) {
2082                    if (stats[i] instanceof AST_Break) return true;
2083                }
2084                return false;
2085            }
2086        }
2087
2088        function find_loop_scope_try() {
2089            var node = compressor.self(), level = 0;
2090            do {
2091                if (!block_scope && node.variables) block_scope = node;
2092                if (node instanceof AST_Catch) {
2093                    if (compressor.parent(level).bfinally) {
2094                        if (!in_try) in_try = {};
2095                        in_try.bfinally = true;
2096                    }
2097                    level++;
2098                } else if (node instanceof AST_Finally) {
2099                    level++;
2100                } else if (node instanceof AST_IterationStatement) {
2101                    in_loop = true;
2102                } else if (node instanceof AST_Scope) {
2103                    scope = node;
2104                    break;
2105                } else if (node instanceof AST_Try) {
2106                    if (!in_try) in_try = {};
2107                    if (node.bcatch) in_try.bcatch = true;
2108                    if (node.bfinally) in_try.bfinally = true;
2109                }
2110            } while (node = compressor.parent(level++));
2111        }
2112
2113        // Search from right to left for assignment-like expressions:
2114        // - `var a = x;`
2115        // - `a = x;`
2116        // - `++a`
2117        // For each candidate, scan from left to right for first usage, then try
2118        // to fold assignment into the site for compression.
2119        // Will not attempt to collapse assignments into or past code blocks
2120        // which are not sequentially executed, e.g. loops and conditionals.
2121        function collapse(statements, compressor) {
2122            if (scope.pinned()) return;
2123            var args;
2124            var assignments = new Dictionary();
2125            var candidates = [];
2126            var changed = false;
2127            var declare_only = new Dictionary();
2128            var force_single;
2129            var stat_index = statements.length;
2130            var scanner = new TreeTransformer(function(node, descend) {
2131                if (abort) return node;
2132                // Skip nodes before `candidate` as quickly as possible
2133                if (!hit) {
2134                    if (node !== hit_stack[hit_index]) return node;
2135                    hit_index++;
2136                    if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner);
2137                    hit = true;
2138                    stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
2139                    if (stop_after === node) abort = true;
2140                    return node;
2141                }
2142                var parent = scanner.parent();
2143                // Stop only if candidate is found within conditional branches
2144                if (!stop_if_hit && in_conditional(node, parent)) {
2145                    stop_if_hit = parent;
2146                }
2147                // Cascade compound assignments
2148                if (compound && scan_lhs && can_replace && !stop_if_hit
2149                    && node instanceof AST_Assign && node.operator != "=" && node.left.equals(lhs)) {
2150                    replaced++;
2151                    changed = true;
2152                    AST_Node.info("Cascading {this} [{start}]", node);
2153                    can_replace = false;
2154                    lvalues = get_lvalues(lhs);
2155                    node.right.transform(scanner);
2156                    clear_write_only(candidate);
2157                    var folded;
2158                    if (abort) {
2159                        folded = candidate;
2160                    } else {
2161                        abort = true;
2162                        folded = make_node(AST_Binary, candidate, {
2163                            operator: compound,
2164                            left: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_binary(candidate) : lhs,
2165                            right: rvalue,
2166                        });
2167                    }
2168                    return make_node(AST_Assign, node, {
2169                        operator: "=",
2170                        left: node.left,
2171                        right: make_node(AST_Binary, node, {
2172                            operator: node.operator.slice(0, -1),
2173                            left: folded,
2174                            right: node.right,
2175                        }),
2176                    });
2177                }
2178                // Stop immediately if these node types are encountered
2179                if (should_stop(node, parent)) {
2180                    abort = true;
2181                    return node;
2182                }
2183                // Skip transient nodes caused by single-use variable replacement
2184                if (node.single_use) return node;
2185                // Replace variable with assignment when found
2186                var hit_rhs;
2187                if (!(node instanceof AST_SymbolDeclaration)
2188                    && (scan_lhs && lhs.equals(node)
2189                        || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
2190                    if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
2191                        if (!hit_rhs && !value_def) abort = true;
2192                        return node;
2193                    }
2194                    if (is_lhs(node, parent)) {
2195                        if (value_def && !hit_rhs) assign_used = true;
2196                        return node;
2197                    }
2198                    if (!hit_rhs && verify_ref && node.fixed !== lhs.fixed) {
2199                        abort = true;
2200                        return node;
2201                    }
2202                    if (value_def) {
2203                        if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced;
2204                        if (!hit_rhs) replaced++;
2205                        return node;
2206                    }
2207                    replaced++;
2208                    changed = abort = true;
2209                    AST_Node.info("Collapsing {this} [{start}]", node);
2210                    if (candidate.TYPE == "Binary") {
2211                        update_symbols(candidate, node);
2212                        return make_node(AST_Assign, candidate, {
2213                            operator: "=",
2214                            left: candidate.right.left,
2215                            right: candidate.operator == "&&" ? make_node(AST_Conditional, candidate, {
2216                                condition: candidate.left,
2217                                consequent: candidate.right.right,
2218                                alternative: node,
2219                            }) : make_node(AST_Conditional, candidate, {
2220                                condition: candidate.left,
2221                                consequent: node,
2222                                alternative: candidate.right.right,
2223                            }),
2224                        });
2225                    }
2226                    if (candidate instanceof AST_UnaryPostfix) return make_node(AST_UnaryPrefix, candidate, {
2227                        operator: candidate.operator,
2228                        expression: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_prefix(candidate) : lhs,
2229                    });
2230                    if (candidate instanceof AST_UnaryPrefix) {
2231                        clear_write_only(candidate);
2232                        return candidate;
2233                    }
2234                    update_symbols(rvalue, node);
2235                    if (candidate instanceof AST_VarDef) {
2236                        var def = candidate.name.definition();
2237                        if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
2238                            def.replaced++;
2239                            return maintain_this_binding(parent, node, rvalue);
2240                        }
2241                        return make_node(AST_Assign, candidate, {
2242                            operator: "=",
2243                            left: node,
2244                            right: rvalue,
2245                        });
2246                    }
2247                    clear_write_only(rvalue);
2248                    var assign = candidate.clone();
2249                    assign.right = rvalue;
2250                    return assign;
2251                }
2252                // Stop signals related to AST_SymbolRef
2253                if (should_stop_ref(node, parent)) {
2254                    abort = true;
2255                    return node;
2256                }
2257                // These node types have child nodes that execute sequentially,
2258                // but are otherwise not safe to scan into or beyond them.
2259                if (is_last_node(node, parent) || may_throw(node)) {
2260                    stop_after = node;
2261                    if (node instanceof AST_Scope) abort = true;
2262                }
2263                // Scan but don't replace inside getter/setter
2264                if (node instanceof AST_Accessor) {
2265                    var replace = can_replace;
2266                    can_replace = false;
2267                    descend(node, scanner);
2268                    can_replace = replace;
2269                    return signal_abort(node);
2270                }
2271                // Scan but don't replace inside destructuring expression
2272                if (node instanceof AST_Destructured) {
2273                    var replace = can_replace;
2274                    can_replace = false;
2275                    descend(node, scanner);
2276                    can_replace = replace;
2277                    return signal_abort(node);
2278                }
2279                // Scan but don't replace inside default value
2280                if (node instanceof AST_DefaultValue) {
2281                    node.name = node.name.transform(scanner);
2282                    var replace = can_replace;
2283                    can_replace = false;
2284                    node.value = node.value.transform(scanner);
2285                    can_replace = replace;
2286                    return signal_abort(node);
2287                }
2288                // Scan but don't replace inside block scope with colliding variable
2289                if (node instanceof AST_BlockScope
2290                    && !(node instanceof AST_Scope)
2291                    && !(node.variables && node.variables.all(function(def) {
2292                        return !enclosed.has(def.name) && !lvalues.has(def.name);
2293                    }))) {
2294                    var replace = can_replace;
2295                    can_replace = false;
2296                    if (!handle_custom_scan_order(node, scanner)) descend(node, scanner);
2297                    can_replace = replace;
2298                    return signal_abort(node);
2299                }
2300                if (handle_custom_scan_order(node, scanner)) return signal_abort(node);
2301            }, signal_abort);
2302            var multi_replacer = new TreeTransformer(function(node) {
2303                if (abort) return node;
2304                // Skip nodes before `candidate` as quickly as possible
2305                if (!hit) {
2306                    if (node !== hit_stack[hit_index]) return node;
2307                    hit_index++;
2308                    switch (hit_stack.length - hit_index) {
2309                      case 0:
2310                        hit = true;
2311                        if (assign_used) return node;
2312                        if (node !== candidate) return node;
2313                        if (node instanceof AST_VarDef) return node;
2314                        def.replaced++;
2315                        var parent = multi_replacer.parent();
2316                        if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
2317                            value_def.replaced++;
2318                            if (rvalue === rhs_value) return List.skip;
2319                            return make_sequence(rhs_value, rhs_value.expressions.slice(0, -1));
2320                        }
2321                        return rvalue;
2322                      case 1:
2323                        if (!assign_used && node.body === candidate) {
2324                            hit = true;
2325                            def.replaced++;
2326                            value_def.replaced++;
2327                            return null;
2328                        }
2329                      default:
2330                        return handle_custom_scan_order(node, multi_replacer);
2331                    }
2332                }
2333                // Replace variable when found
2334                if (node instanceof AST_SymbolRef && node.definition() === def) {
2335                    if (is_lhs(node, multi_replacer.parent())) return node;
2336                    if (!--replaced) abort = true;
2337                    AST_Node.info("Replacing {this} [{start}]", node);
2338                    var ref = rvalue.clone();
2339                    ref.scope = node.scope;
2340                    ref.reference();
2341                    if (replaced == assign_pos) {
2342                        abort = true;
2343                        return make_node(AST_Assign, candidate, {
2344                            operator: "=",
2345                            left: node,
2346                            right: ref,
2347                        });
2348                    }
2349                    def.replaced++;
2350                    return ref;
2351                }
2352                // Skip (non-executed) functions and (leading) default case in switch statements
2353                if (node instanceof AST_Default || node instanceof AST_Scope) return node;
2354            }, function(node) {
2355                return patch_sequence(node, multi_replacer);
2356            });
2357            while (--stat_index >= 0) {
2358                // Treat parameters as collapsible in IIFE, i.e.
2359                //   function(a, b){ ... }(x());
2360                // would be translated into equivalent assignments:
2361                //   var a = x(), b = undefined;
2362                if (stat_index == 0 && compressor.option("unused")) extract_args();
2363                // Find collapsible assignments
2364                var hit_stack = [];
2365                extract_candidates(statements[stat_index]);
2366                while (candidates.length > 0) {
2367                    hit_stack = candidates.pop();
2368                    var hit_index = 0;
2369                    var candidate = hit_stack[hit_stack.length - 1];
2370                    var assign_pos = -1;
2371                    var assign_used = false;
2372                    var verify_ref = false;
2373                    var remaining;
2374                    var value_def = null;
2375                    var stop_after = null;
2376                    var stop_if_hit = null;
2377                    var lhs = get_lhs(candidate);
2378                    var side_effects = lhs && lhs.has_side_effects(compressor);
2379                    var scan_lhs = lhs && (!side_effects || lhs instanceof AST_SymbolRef)
2380                            && !is_lhs_read_only(lhs, compressor);
2381                    var scan_rhs = foldable(candidate);
2382                    if (!scan_lhs && !scan_rhs) continue;
2383                    var compound = candidate instanceof AST_Assign && candidate.operator.slice(0, -1);
2384                    var funarg = candidate.name instanceof AST_SymbolFunarg;
2385                    var may_throw = return_false;
2386                    if (candidate.may_throw(compressor)) {
2387                        if (funarg && is_async(scope)) continue;
2388                        may_throw = in_try ? function(node) {
2389                            return node.has_side_effects(compressor);
2390                        } : side_effects_external;
2391                    }
2392                    var read_toplevel = false;
2393                    var modify_toplevel = false;
2394                    // Locate symbols which may execute code outside of scanning range
2395                    var enclosed = new Dictionary();
2396                    var well_defined = true;
2397                    var lvalues = get_lvalues(candidate);
2398                    var lhs_local = is_lhs_local(lhs);
2399                    var rhs_value = get_rvalue(candidate);
2400                    var rvalue = rhs_value;
2401                    if (!side_effects) {
2402                        if (!compound && rvalue instanceof AST_Sequence) rvalue = rvalue.tail_node();
2403                        side_effects = value_has_side_effects();
2404                    }
2405                    var check_destructured = in_try || !lhs_local ? function(node) {
2406                        return node instanceof AST_Destructured;
2407                    } : return_false;
2408                    var replace_all = replace_all_symbols(candidate);
2409                    var hit = funarg;
2410                    var abort = false;
2411                    var replaced = 0;
2412                    var can_replace = !args || !hit;
2413                    if (!can_replace) {
2414                        for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) {
2415                            if (args[j]) args[j].transform(scanner);
2416                        }
2417                        can_replace = true;
2418                    }
2419                    for (var i = stat_index; !abort && i < statements.length; i++) {
2420                        statements[i].transform(scanner);
2421                    }
2422                    if (value_def) {
2423                        if (!replaced || remaining > replaced + assign_used) {
2424                            candidates.push(hit_stack);
2425                            force_single = true;
2426                            continue;
2427                        }
2428                        if (replaced == assign_pos) assign_used = true;
2429                        var def = lhs.definition();
2430                        abort = false;
2431                        hit_index = 0;
2432                        hit = funarg;
2433                        for (var i = stat_index; !abort && i < statements.length; i++) {
2434                            if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
2435                        }
2436                        replaced = candidate instanceof AST_VarDef
2437                            && candidate === hit_stack[hit_stack.length - 1]
2438                            && def.references.length == def.replaced
2439                            && !compressor.exposed(def);
2440                        value_def.last_ref = false;
2441                        value_def.single_use = false;
2442                        changed = true;
2443                    }
2444                    if (replaced) remove_candidate(candidate);
2445                }
2446            }
2447            return changed;
2448
2449            function signal_abort(node) {
2450                if (abort) return node;
2451                if (stop_after === node) abort = true;
2452                if (stop_if_hit === node) stop_if_hit = null;
2453                return node;
2454            }
2455
2456            function handle_custom_scan_order(node, tt) {
2457                if (!(node instanceof AST_BlockScope)) return;
2458                // Skip (non-executed) functions
2459                if (node instanceof AST_Scope) return node;
2460                // Scan computed keys, static fields & initializers in class
2461                if (node instanceof AST_Class) {
2462                    if (node.name) node.name = node.name.transform(tt);
2463                    if (!abort && node.extends) node.extends = node.extends.transform(tt);
2464                    var fields = [], stats = [];
2465                    for (var i = 0; !abort && i < node.properties.length; i++) {
2466                        var prop = node.properties[i];
2467                        if (prop.key instanceof AST_Node) prop.key = prop.key.transform(tt);
2468                        if (!prop.static) continue;
2469                        if (prop instanceof AST_ClassField) {
2470                            if (prop.value) fields.push(prop);
2471                        } else if (prop instanceof AST_ClassInit) {
2472                            [].push.apply(stats, prop.value.body);
2473                        }
2474                    }
2475                    for (var i = 0; !abort && i < stats.length; i++) {
2476                        stats[i].transform(tt);
2477                    }
2478                    for (var i = 0; !abort && i < fields.length; i++) {
2479                        var prop = fields[i];
2480                        prop.value = prop.value.transform(tt);
2481                    }
2482                    return node;
2483                }
2484                // Scan object only in a for-in/of statement
2485                if (node instanceof AST_ForEnumeration) {
2486                    node.object = node.object.transform(tt);
2487                    abort = true;
2488                    return node;
2489                }
2490                // Scan first case expression only in a switch statement
2491                if (node instanceof AST_Switch) {
2492                    node.expression = node.expression.transform(tt);
2493                    for (var i = 0; !abort && i < node.body.length; i++) {
2494                        var branch = node.body[i];
2495                        if (branch instanceof AST_Case) {
2496                            if (!hit) {
2497                                if (branch !== hit_stack[hit_index]) continue;
2498                                hit_index++;
2499                            }
2500                            branch.expression = branch.expression.transform(tt);
2501                            if (!replace_all) break;
2502                            scan_rhs = false;
2503                        }
2504                    }
2505                    abort = true;
2506                    return node;
2507                }
2508            }
2509
2510            function is_direct_assignment(node, parent) {
2511                if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node;
2512                if (parent instanceof AST_DefaultValue) return parent.name === node;
2513                if (parent instanceof AST_DestructuredArray) return true;
2514                if (parent instanceof AST_DestructuredKeyVal) return parent.value === node;
2515            }
2516
2517            function should_stop(node, parent) {
2518                if (node === rvalue) return true;
2519                if (parent instanceof AST_For) {
2520                    if (node !== parent.init) return true;
2521                }
2522                if (node instanceof AST_Assign) {
2523                    return node.operator != "=" && lhs.equals(node.left);
2524                }
2525                if (node instanceof AST_Call) {
2526                    if (!(lhs instanceof AST_PropAccess)) return false;
2527                    if (!lhs.equals(node.expression)) return false;
2528                    return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
2529                }
2530                if (node instanceof AST_Class) return !compressor.has_directive("use strict");
2531                if (node instanceof AST_Debugger) return true;
2532                if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
2533                if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node;
2534                if (node instanceof AST_DWLoop) return true;
2535                if (node instanceof AST_LoopControl) return true;
2536                if (node instanceof AST_Try) return true;
2537                if (node instanceof AST_With) return true;
2538                return false;
2539            }
2540
2541            function should_stop_ref(node, parent) {
2542                if (!(node instanceof AST_SymbolRef)) return false;
2543                if (node.is_declared(compressor)) {
2544                    if (node.fixed_value()) return false;
2545                    if (can_drop_symbol(node)) {
2546                        return !(parent instanceof AST_PropAccess && parent.expression === node)
2547                            && is_arguments(node.definition());
2548                    }
2549                } else if (is_direct_assignment(node, parent)) {
2550                    return false;
2551                }
2552                if (!replace_all) return true;
2553                scan_rhs = false;
2554                return false;
2555            }
2556
2557            function in_conditional(node, parent) {
2558                if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
2559                if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
2560                if (parent instanceof AST_Call) return parent.optional && parent.expression !== node;
2561                if (parent instanceof AST_Case) return parent.expression !== node;
2562                if (parent instanceof AST_Conditional) return parent.condition !== node;
2563                if (parent instanceof AST_If) return parent.condition !== node;
2564                if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node;
2565            }
2566
2567            function is_last_node(node, parent) {
2568                if (node instanceof AST_Await) return true;
2569                if (node.TYPE == "Binary") return !can_drop_op(node.operator, node.right, compressor);
2570                if (node instanceof AST_Call) {
2571                    var def, fn = node.expression;
2572                    if (fn instanceof AST_SymbolRef) {
2573                        def = fn.definition();
2574                        fn = fn.fixed_value();
2575                    }
2576                    if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor);
2577                    if (def && recursive_ref(compressor, def, fn)) return true;
2578                    if (fn.collapse_scanning) return false;
2579                    fn.collapse_scanning = true;
2580                    var replace = can_replace;
2581                    can_replace = false;
2582                    var after = stop_after;
2583                    var if_hit = stop_if_hit;
2584                    for (var i = 0; !abort && i < fn.argnames.length; i++) {
2585                        if (arg_may_throw(reject, fn.argnames[i], node.args[i])) abort = true;
2586                    }
2587                    if (!abort) {
2588                        if (fn.rest && arg_may_throw(reject, fn.rest, make_node(AST_Array, node, {
2589                            elements: node.args.slice(i),
2590                        }))) {
2591                            abort = true;
2592                        } else if (is_arrow(fn) && fn.value) {
2593                            fn.value.transform(scanner);
2594                        } else for (var i = 0; !abort && i < fn.body.length; i++) {
2595                            var stat = fn.body[i];
2596                            if (stat instanceof AST_Return) {
2597                                if (stat.value) stat.value.transform(scanner);
2598                                break;
2599                            }
2600                            stat.transform(scanner);
2601                        }
2602                    }
2603                    stop_if_hit = if_hit;
2604                    stop_after = after;
2605                    can_replace = replace;
2606                    fn.collapse_scanning = false;
2607                    if (!abort) return false;
2608                    abort = false;
2609                    return true;
2610                }
2611                if (node instanceof AST_Class) {
2612                    if (!in_try) return false;
2613                    var base = node.extends;
2614                    if (!base) return false;
2615                    if (base instanceof AST_SymbolRef) base = base.fixed_value();
2616                    return !safe_for_extends(base);
2617                }
2618                if (node instanceof AST_Exit) {
2619                    if (in_try) {
2620                        if (in_try.bfinally) return true;
2621                        if (in_try.bcatch && node instanceof AST_Throw) return true;
2622                    }
2623                    return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
2624                }
2625                if (node instanceof AST_Function) {
2626                    return compressor.option("ie") && node.name && lvalues.has(node.name.name);
2627                }
2628                if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent);
2629                if (node instanceof AST_PropAccess) {
2630                    if (side_effects) return true;
2631                    var exp = node.expression;
2632                    if (exp instanceof AST_SymbolRef && is_arguments(exp.definition())) return true;
2633                    if (compressor.option("unsafe")) {
2634                        if (is_undeclared_ref(exp) && global_names[exp.name]) return false;
2635                        if (is_static_fn(exp)) return false;
2636                    }
2637                    if (!well_defined) return true;
2638                    if (value_def) return false;
2639                    if (!in_try && lhs_local) return false;
2640                    if (node.optional) return false;
2641                    return exp.may_throw_on_access(compressor);
2642                }
2643                if (node instanceof AST_Spread) return true;
2644                if (node instanceof AST_SymbolRef) {
2645                    if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent);
2646                    if (side_effects && may_modify(node)) return true;
2647                    var def = node.definition();
2648                    return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
2649                }
2650                if (node instanceof AST_Template) return !node.is_expr_pure(compressor);
2651                if (node instanceof AST_VarDef) {
2652                    if (check_destructured(node.name)) return true;
2653                    return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
2654                        return node instanceof AST_SymbolDeclaration
2655                            && (lvalues.has(node.name) || side_effects && may_modify(node));
2656                    }, true);
2657                }
2658                if (node instanceof AST_Yield) return true;
2659                var sym = is_lhs(node.left, node);
2660                if (!sym) return false;
2661                if (sym instanceof AST_PropAccess) return true;
2662                if (check_destructured(sym)) return true;
2663                return sym.match_symbol(function(node) {
2664                    return node instanceof AST_SymbolRef
2665                        && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition()));
2666                }, true);
2667
2668                function reject(node) {
2669                    node.transform(scanner);
2670                    return abort;
2671                }
2672            }
2673
2674            function arg_may_throw(reject, node, value) {
2675                if (node instanceof AST_DefaultValue) {
2676                    return reject(node.value)
2677                        || arg_may_throw(reject, node.name, node.value)
2678                        || !is_undefined(value) && arg_may_throw(reject, node.name, value);
2679                }
2680                if (!value) return !(node instanceof AST_Symbol);
2681                if (node instanceof AST_Destructured) {
2682                    if (node.rest && arg_may_throw(reject, node.rest)) return true;
2683                    if (node instanceof AST_DestructuredArray) {
2684                        if (value instanceof AST_Array) return !all(node.elements, function(element, index) {
2685                            return !arg_may_throw(reject, element, value[index]);
2686                        });
2687                        if (!value.is_string(compressor)) return true;
2688                        return !all(node.elements, function(element) {
2689                            return !arg_may_throw(reject, element);
2690                        });
2691                    }
2692                    if (node instanceof AST_DestructuredObject) {
2693                        if (value.may_throw_on_access(compressor)) return true;
2694                        return !all(node.properties, function(prop) {
2695                            if (prop.key instanceof AST_Node && reject(prop.key)) return false;
2696                            return !arg_may_throw(reject, prop.value);
2697                        });
2698                    }
2699                }
2700            }
2701
2702            function extract_args() {
2703                if (in_iife_single === false) return;
2704                var iife = compressor.parent(), fn = compressor.self();
2705                if (in_iife_single === undefined) {
2706                    if (!(fn instanceof AST_LambdaExpression)
2707                        || is_generator(fn)
2708                        || fn.uses_arguments
2709                        || fn.pinned()
2710                        || !(iife instanceof AST_Call)
2711                        || iife.expression !== fn
2712                        || !all(iife.args, function(arg) {
2713                            return !(arg instanceof AST_Spread);
2714                        })) {
2715                        in_iife_single = false;
2716                        return;
2717                    }
2718                    if (!is_iife_single(iife)) return;
2719                    in_iife_single = true;
2720                }
2721                var fn_strict = fn.in_strict_mode(compressor)
2722                    && !fn.parent_scope.resolve(true).in_strict_mode(compressor);
2723                var has_await;
2724                if (is_async(fn)) {
2725                    has_await = function(node) {
2726                        return node instanceof AST_Symbol && node.name == "await";
2727                    };
2728                    iife_in_try = true;
2729                } else {
2730                    has_await = function(node) {
2731                        return node instanceof AST_Await && !tw.find_parent(AST_Scope);
2732                    };
2733                    if (iife_in_try === undefined) iife_in_try = find_try(compressor, 1, iife, null, true, true);
2734                }
2735                var arg_scope = null;
2736                var tw = new TreeWalker(function(node, descend) {
2737                    if (!arg) return true;
2738                    if (has_await(node) || node instanceof AST_Yield) {
2739                        arg = null;
2740                        return true;
2741                    }
2742                    if (node instanceof AST_ObjectIdentity) {
2743                        if (fn_strict || !arg_scope) arg = null;
2744                        return true;
2745                    }
2746                    if (node instanceof AST_SymbolRef) {
2747                        var def;
2748                        if (node.in_arg && !is_safe_lexical(node.definition())
2749                            || (def = fn.variables.get(node.name)) && def !== node.definition()) {
2750                            arg = null;
2751                        }
2752                        return true;
2753                    }
2754                    if (node instanceof AST_Scope && !is_arrow(node)) {
2755                        var save_scope = arg_scope;
2756                        arg_scope = node;
2757                        descend();
2758                        arg_scope = save_scope;
2759                        return true;
2760                    }
2761                });
2762                args = iife.args.slice();
2763                var len = args.length;
2764                var names = new Dictionary();
2765                for (var i = fn.argnames.length; --i >= 0;) {
2766                    var sym = fn.argnames[i];
2767                    var arg = args[i];
2768                    var value = null;
2769                    if (sym instanceof AST_DefaultValue) {
2770                        value = sym.value;
2771                        sym = sym.name;
2772                        args[len + i] = value;
2773                    }
2774                    if (sym instanceof AST_Destructured) {
2775                        if (iife_in_try && arg_may_throw(function(node) {
2776                            return node.has_side_effects(compressor);
2777                        }, sym, arg)) {
2778                            candidates.length = 0;
2779                            break;
2780                        }
2781                        args[len + i] = fn.argnames[i];
2782                        continue;
2783                    }
2784                    if (names.has(sym.name)) continue;
2785                    names.set(sym.name, true);
2786                    if (value) arg = is_undefined(arg) ? value : null;
2787                    if (!arg && !value) {
2788                        arg = make_node(AST_Undefined, sym).transform(compressor);
2789                    } else if (arg instanceof AST_Lambda && arg.pinned()) {
2790                        arg = null;
2791                    } else if (arg) {
2792                        arg.walk(tw);
2793                    }
2794                    if (!arg) continue;
2795                    var candidate = make_node(AST_VarDef, sym, {
2796                        name: sym,
2797                        value: arg,
2798                    });
2799                    candidate.name_index = i;
2800                    candidate.arg_index = value ? len + i : i;
2801                    candidates.unshift([ candidate ]);
2802                }
2803                if (fn.rest) args.push(fn.rest);
2804            }
2805
2806            function extract_candidates(expr, unused) {
2807                hit_stack.push(expr);
2808                if (expr instanceof AST_Array) {
2809                    expr.elements.forEach(function(node) {
2810                        extract_candidates(node, unused);
2811                    });
2812                } else if (expr instanceof AST_Assign) {
2813                    var lhs = expr.left;
2814                    if (!(lhs instanceof AST_Destructured)) candidates.push(hit_stack.slice());
2815                    extract_candidates(lhs);
2816                    extract_candidates(expr.right);
2817                    if (lhs instanceof AST_SymbolRef && expr.operator == "=") {
2818                        assignments.set(lhs.name, (assignments.get(lhs.name) || 0) + 1);
2819                    }
2820                } else if (expr instanceof AST_Await) {
2821                    extract_candidates(expr.expression, unused);
2822                } else if (expr instanceof AST_Binary) {
2823                    var lazy = lazy_op[expr.operator];
2824                    if (unused
2825                        && lazy
2826                        && expr.operator != "??"
2827                        && expr.right instanceof AST_Assign
2828                        && expr.right.operator == "="
2829                        && !(expr.right.left instanceof AST_Destructured)) {
2830                        candidates.push(hit_stack.slice());
2831                    }
2832                    extract_candidates(expr.left, !lazy && unused);
2833                    extract_candidates(expr.right, unused);
2834                } else if (expr instanceof AST_Call) {
2835                    extract_candidates(expr.expression);
2836                    expr.args.forEach(extract_candidates);
2837                } else if (expr instanceof AST_Case) {
2838                    extract_candidates(expr.expression);
2839                } else if (expr instanceof AST_Conditional) {
2840                    extract_candidates(expr.condition);
2841                    extract_candidates(expr.consequent, unused);
2842                    extract_candidates(expr.alternative, unused);
2843                } else if (expr instanceof AST_Definitions) {
2844                    expr.definitions.forEach(extract_candidates);
2845                } else if (expr instanceof AST_Dot) {
2846                    extract_candidates(expr.expression);
2847                } else if (expr instanceof AST_DWLoop) {
2848                    extract_candidates(expr.condition);
2849                    if (!(expr.body instanceof AST_Block)) {
2850                        extract_candidates(expr.body);
2851                    }
2852                } else if (expr instanceof AST_Exit) {
2853                    if (expr.value) extract_candidates(expr.value);
2854                } else if (expr instanceof AST_For) {
2855                    if (expr.init) extract_candidates(expr.init, true);
2856                    if (expr.condition) extract_candidates(expr.condition);
2857                    if (expr.step) extract_candidates(expr.step, true);
2858                    if (!(expr.body instanceof AST_Block)) {
2859                        extract_candidates(expr.body);
2860                    }
2861                } else if (expr instanceof AST_ForEnumeration) {
2862                    extract_candidates(expr.object);
2863                    if (!(expr.body instanceof AST_Block)) {
2864                        extract_candidates(expr.body);
2865                    }
2866                } else if (expr instanceof AST_If) {
2867                    extract_candidates(expr.condition);
2868                    if (!(expr.body instanceof AST_Block)) {
2869                        extract_candidates(expr.body);
2870                    }
2871                    if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
2872                        extract_candidates(expr.alternative);
2873                    }
2874                } else if (expr instanceof AST_Object) {
2875                    expr.properties.forEach(function(prop) {
2876                        hit_stack.push(prop);
2877                        if (prop.key instanceof AST_Node) extract_candidates(prop.key);
2878                        if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value, unused);
2879                        hit_stack.pop();
2880                    });
2881                } else if (expr instanceof AST_Sequence) {
2882                    var end = expr.expressions.length - (unused ? 0 : 1);
2883                    expr.expressions.forEach(function(node, index) {
2884                        extract_candidates(node, index < end);
2885                    });
2886                } else if (expr instanceof AST_SimpleStatement) {
2887                    extract_candidates(expr.body, true);
2888                } else if (expr instanceof AST_Spread) {
2889                    extract_candidates(expr.expression);
2890                } else if (expr instanceof AST_Sub) {
2891                    extract_candidates(expr.expression);
2892                    extract_candidates(expr.property);
2893                } else if (expr instanceof AST_Switch) {
2894                    extract_candidates(expr.expression);
2895                    expr.body.forEach(extract_candidates);
2896                } else if (expr instanceof AST_Unary) {
2897                    if (UNARY_POSTFIX[expr.operator]) {
2898                        candidates.push(hit_stack.slice());
2899                    } else {
2900                        extract_candidates(expr.expression);
2901                    }
2902                } else if (expr instanceof AST_VarDef) {
2903                    if (expr.name instanceof AST_SymbolVar) {
2904                        if (expr.value) {
2905                            var def = expr.name.definition();
2906                            if (def.references.length > def.replaced) {
2907                                candidates.push(hit_stack.slice());
2908                            }
2909                        } else {
2910                            declare_only.set(expr.name.name, (declare_only.get(expr.name.name) || 0) + 1);
2911                        }
2912                    }
2913                    if (expr.value) extract_candidates(expr.value);
2914                } else if (expr instanceof AST_Yield) {
2915                    if (expr.expression) extract_candidates(expr.expression);
2916                }
2917                hit_stack.pop();
2918            }
2919
2920            function find_stop(node, level) {
2921                var parent = scanner.parent(level);
2922                if (parent instanceof AST_Array) return node;
2923                if (parent instanceof AST_Assign) return node;
2924                if (parent instanceof AST_Await) return node;
2925                if (parent instanceof AST_Binary) return node;
2926                if (parent instanceof AST_Call) return node;
2927                if (parent instanceof AST_Case) return node;
2928                if (parent instanceof AST_Conditional) return node;
2929                if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2930                if (parent instanceof AST_Exit) return node;
2931                if (parent instanceof AST_If) return node;
2932                if (parent instanceof AST_IterationStatement) return node;
2933                if (parent instanceof AST_ObjectProperty) return node;
2934                if (parent instanceof AST_PropAccess) return node;
2935                if (parent instanceof AST_Sequence) {
2936                    return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
2937                }
2938                if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2939                if (parent instanceof AST_Spread) return node;
2940                if (parent instanceof AST_Switch) return node;
2941                if (parent instanceof AST_Unary) return node;
2942                if (parent instanceof AST_VarDef) return node;
2943                if (parent instanceof AST_Yield) return node;
2944                return null;
2945            }
2946
2947            function find_stop_logical(parent, op, level) {
2948                var node;
2949                do {
2950                    node = parent;
2951                    parent = scanner.parent(++level);
2952                } while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op
2953                    || parent instanceof AST_Binary && parent.operator == op);
2954                return node;
2955            }
2956
2957            function find_stop_expr(expr, cont, node, parent, level) {
2958                var replace = can_replace;
2959                can_replace = false;
2960                var after = stop_after;
2961                var if_hit = stop_if_hit;
2962                var stack = scanner.stack;
2963                scanner.stack = [ parent ];
2964                expr.transform(scanner);
2965                scanner.stack = stack;
2966                stop_if_hit = if_hit;
2967                stop_after = after;
2968                can_replace = replace;
2969                if (abort) {
2970                    abort = false;
2971                    return node;
2972                }
2973                return cont(parent, level + 1);
2974            }
2975
2976            function find_stop_value(node, level) {
2977                var parent = scanner.parent(level);
2978                if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
2979                if (parent instanceof AST_Assign) {
2980                    if (may_throw(parent)) return node;
2981                    if (parent.left.match_symbol(function(ref) {
2982                        return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name);
2983                    })) return node;
2984                    var op;
2985                    if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) {
2986                        return find_stop_value(parent, level + 1);
2987                    }
2988                    return find_stop_logical(parent, op, level);
2989                }
2990                if (parent instanceof AST_Await) return find_stop_value(parent, level + 1);
2991                if (parent instanceof AST_Binary) {
2992                    var op;
2993                    if (parent.left === node || !lazy_op[op = parent.operator]) {
2994                        return find_stop_value(parent, level + 1);
2995                    }
2996                    return find_stop_logical(parent, op, level);
2997                }
2998                if (parent instanceof AST_Call) return parent;
2999                if (parent instanceof AST_Case) {
3000                    if (parent.expression !== node) return node;
3001                    return find_stop_value(parent, level + 1);
3002                }
3003                if (parent instanceof AST_Conditional) {
3004                    if (parent.condition !== node) return node;
3005                    return find_stop_value(parent, level + 1);
3006                }
3007                if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
3008                if (parent instanceof AST_Do) return node;
3009                if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
3010                if (parent instanceof AST_For) {
3011                    if (parent.init !== node && parent.condition !== node) return node;
3012                    return find_stop_value(parent, level + 1);
3013                }
3014                if (parent instanceof AST_ForEnumeration) {
3015                    if (parent.init !== node) return node;
3016                    return find_stop_value(parent, level + 1);
3017                }
3018                if (parent instanceof AST_If) {
3019                    if (parent.condition !== node) return node;
3020                    return find_stop_value(parent, level + 1);
3021                }
3022                if (parent instanceof AST_ObjectProperty) {
3023                    var obj = scanner.parent(level + 1);
3024                    return all(obj.properties, function(prop) {
3025                        return prop instanceof AST_ObjectKeyVal;
3026                    }) ? find_stop_value(obj, level + 2) : obj;
3027                }
3028                if (parent instanceof AST_PropAccess) {
3029                    var exp = parent.expression;
3030                    return exp === node ? find_stop_value(parent, level + 1) : node;
3031                }
3032                if (parent instanceof AST_Sequence) {
3033                    return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
3034                }
3035                if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
3036                if (parent instanceof AST_Spread) return find_stop_value(parent, level + 1);
3037                if (parent instanceof AST_Switch) {
3038                    if (parent.expression !== node) return node;
3039                    return find_stop_value(parent, level + 1);
3040                }
3041                if (parent instanceof AST_Unary) {
3042                    if (parent.operator == "delete") return node;
3043                    return find_stop_value(parent, level + 1);
3044                }
3045                if (parent instanceof AST_VarDef) return parent.name.match_symbol(function(sym) {
3046                    return sym instanceof AST_SymbolDeclaration && (lhs.name == sym.name || value_def.name == sym.name);
3047                }) ? node : find_stop_value(parent, level + 1);
3048                if (parent instanceof AST_While) {
3049                    if (parent.condition !== node) return node;
3050                    return find_stop_value(parent, level + 1);
3051                }
3052                if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1);
3053                return null;
3054            }
3055
3056            function find_stop_unused(node, level) {
3057                var parent = scanner.parent(level);
3058                if (is_last_node(node, parent)) return node;
3059                if (in_conditional(node, parent)) return node;
3060                if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
3061                if (parent instanceof AST_Assign) return check_assignment(parent.left);
3062                if (parent instanceof AST_Await) return node;
3063                if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
3064                if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
3065                if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
3066                if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
3067                if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
3068                if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
3069                if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
3070                if (parent instanceof AST_IterationStatement) return node;
3071                if (parent instanceof AST_ObjectProperty) {
3072                    var obj = scanner.parent(level + 1);
3073                    return all(obj.properties, function(prop) {
3074                        return prop instanceof AST_ObjectKeyVal;
3075                    }) ? find_stop_unused(obj, level + 2) : obj;
3076                }
3077                if (parent instanceof AST_PropAccess) {
3078                    var exp = parent.expression;
3079                    if (exp === node) return find_stop_unused(parent, level + 1);
3080                    return find_stop_expr(exp, find_stop_unused, node, parent, level);
3081                }
3082                if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
3083                if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
3084                if (parent instanceof AST_Spread) return node;
3085                if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
3086                if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
3087                if (parent instanceof AST_VarDef) return check_assignment(parent.name);
3088                if (parent instanceof AST_Yield) return node;
3089                return null;
3090
3091                function check_assignment(lhs) {
3092                    if (may_throw(parent)) return node;
3093                    if (lhs !== node && lhs instanceof AST_Destructured) {
3094                        return find_stop_expr(lhs, find_stop_unused, node, parent, level);
3095                    }
3096                    return find_stop_unused(parent, level + 1);
3097                }
3098            }
3099
3100            function mangleable_var(rhs) {
3101                if (force_single) {
3102                    force_single = false;
3103                    return;
3104                }
3105                if (remaining < 1) return;
3106                rhs = rhs.tail_node();
3107                var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
3108                if (!(value instanceof AST_SymbolRef)) return;
3109                var def = value.definition();
3110                if (def.undeclared) return;
3111                if (is_arguments(def)) return;
3112                if (value !== rhs) {
3113                    if (is_lhs_read_only(value, compressor)) return;
3114                    var referenced = def.references.length - def.replaced;
3115                    if (referenced < 2) return;
3116                    var expr = candidate.clone();
3117                    expr[expr instanceof AST_Assign ? "right" : "value"] = value;
3118                    if (candidate.name_index >= 0) {
3119                        expr.name_index = candidate.name_index;
3120                        expr.arg_index = candidate.arg_index;
3121                    }
3122                    candidate = expr;
3123                }
3124                return value_def = def;
3125            }
3126
3127            function remaining_refs(def) {
3128                return def.references.length - def.replaced - (assignments.get(def.name) || 0);
3129            }
3130
3131            function get_lhs(expr) {
3132                if (expr instanceof AST_Assign) {
3133                    var lhs = expr.left;
3134                    if (!(lhs instanceof AST_SymbolRef)) return lhs;
3135                    var def = lhs.definition();
3136                    if (scope.uses_arguments && is_funarg(def)) return lhs;
3137                    if (compressor.exposed(def)) return lhs;
3138                    remaining = remaining_refs(def);
3139                    if (def.fixed && lhs.fixed) {
3140                        var matches = def.references.filter(function(ref) {
3141                            return ref.fixed === lhs.fixed;
3142                        }).length - 1;
3143                        if (matches < remaining) {
3144                            remaining = matches;
3145                            assign_pos = 0;
3146                            verify_ref = true;
3147                        }
3148                    }
3149                    if (expr.operator == "=") mangleable_var(expr.right);
3150                    return lhs;
3151                }
3152                if (expr instanceof AST_Binary) return expr.right.left;
3153                if (expr instanceof AST_Unary) return expr.expression;
3154                if (expr instanceof AST_VarDef) {
3155                    var lhs = expr.name;
3156                    var def = lhs.definition();
3157                    if (def.const_redefs) return;
3158                    if (!member(lhs, def.orig)) return;
3159                    if (scope.uses_arguments && is_funarg(def)) return;
3160                    var declared = def.orig.length - def.eliminated - (declare_only.get(def.name) || 0);
3161                    remaining = remaining_refs(def);
3162                    if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) {
3163                        if (!ref.fixed) return true;
3164                        if (!ref.fixed.assigns) return true;
3165                        var assign = ref.fixed.assigns[0];
3166                        return assign === lhs || get_rvalue(assign) === expr.value;
3167                    }).length);
3168                    if (declared > 1 && !(lhs instanceof AST_SymbolFunarg)) {
3169                        mangleable_var(expr.value);
3170                        return make_node(AST_SymbolRef, lhs);
3171                    }
3172                    if (mangleable_var(expr.value) || remaining == 1 && !compressor.exposed(def)) {
3173                        return make_node(AST_SymbolRef, lhs);
3174                    }
3175                    return;
3176                }
3177            }
3178
3179            function get_rvalue(expr) {
3180                if (expr instanceof AST_Assign) return expr.right;
3181                if (expr instanceof AST_Binary) {
3182                    var node = expr.clone();
3183                    node.right = expr.right.right;
3184                    return node;
3185                }
3186                if (expr instanceof AST_VarDef) return expr.value;
3187            }
3188
3189            function invariant(expr) {
3190                if (expr instanceof AST_Array) return false;
3191                if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
3192                    return invariant(expr.left) && invariant(expr.right);
3193                }
3194                if (expr instanceof AST_Call) return false;
3195                if (expr instanceof AST_Conditional) {
3196                    return invariant(expr.consequent) && invariant(expr.alternative);
3197                }
3198                if (expr instanceof AST_Object) return false;
3199                return !expr.has_side_effects(compressor);
3200            }
3201
3202            function foldable(expr) {
3203                if (expr instanceof AST_Assign && expr.right.single_use) return;
3204                var lhs_ids = Object.create(null);
3205                var marker = new TreeWalker(function(node) {
3206                    if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true;
3207                });
3208                while (expr instanceof AST_Assign && expr.operator == "=") {
3209                    expr.left.walk(marker);
3210                    expr = expr.right;
3211                }
3212                if (expr instanceof AST_ObjectIdentity) return rhs_exact_match;
3213                if (expr instanceof AST_SymbolRef) {
3214                    var value = expr.evaluate(compressor);
3215                    if (value === expr) return rhs_exact_match;
3216                    return rhs_fuzzy_match(value, rhs_exact_match);
3217                }
3218                if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
3219                if (expr.is_constant()) {
3220                    var ev = expr.evaluate(compressor);
3221                    if (!(ev instanceof AST_Node)) return rhs_fuzzy_match(ev, rhs_exact_match);
3222                }
3223                if (!(lhs instanceof AST_SymbolRef)) return false;
3224                if (!invariant(expr)) return false;
3225                var circular;
3226                expr.walk(new TreeWalker(function(node) {
3227                    if (circular) return true;
3228                    if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true;
3229                }));
3230                return !circular && rhs_exact_match;
3231
3232                function rhs_exact_match(node) {
3233                    return expr.equals(node);
3234                }
3235            }
3236
3237            function rhs_fuzzy_match(value, fallback) {
3238                return function(node, tw) {
3239                    if (tw.in_boolean_context()) {
3240                        if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
3241                            return true;
3242                        }
3243                        if (node.is_constant()) {
3244                            var ev = node.evaluate(compressor);
3245                            if (!(ev instanceof AST_Node)) return !ev == !value;
3246                        }
3247                    }
3248                    return fallback(node);
3249                };
3250            }
3251
3252            function clear_write_only(assign) {
3253                while (assign.write_only) {
3254                    assign.write_only = false;
3255                    if (!(assign instanceof AST_Assign)) break;
3256                    assign = assign.right;
3257                }
3258            }
3259
3260            function update_symbols(value, node) {
3261                var scope = node.scope || find_scope(scanner) || block_scope;
3262                value.walk(new TreeWalker(function(node) {
3263                    if (node instanceof AST_BlockScope) return true;
3264                    if (node instanceof AST_Symbol) node.scope = scope;
3265                }));
3266            }
3267
3268            function may_be_global(node) {
3269                if (node instanceof AST_SymbolRef) {
3270                    node = node.fixed_value();
3271                    if (!node) return true;
3272                }
3273                if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
3274                return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity;
3275            }
3276
3277            function get_lvalues(expr) {
3278                var lvalues = new Dictionary();
3279                if (expr instanceof AST_VarDef) {
3280                    if (!expr.name.definition().fixed) well_defined = false;
3281                    lvalues.add(expr.name.name, lhs);
3282                }
3283                var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict");
3284                var scan_toplevel = scope instanceof AST_Toplevel;
3285                var tw = new TreeWalker(function(node) {
3286                    var value;
3287                    if (node instanceof AST_SymbolRef) {
3288                        value = node.fixed_value();
3289                        if (!value) {
3290                            value = node;
3291                            var def = node.definition();
3292                            var escaped = node.fixed && node.fixed.escaped || def.escaped;
3293                            if (!def.undeclared
3294                                && (def.assignments || !escaped || escaped.cross_scope)
3295                                && (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) {
3296                                well_defined = false;
3297                            }
3298                        }
3299                    } else if (node instanceof AST_ObjectIdentity) {
3300                        value = node;
3301                    }
3302                    if (value) {
3303                        lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
3304                    } else if (node instanceof AST_Lambda) {
3305                        for (var level = 0, parent, child = node; parent = tw.parent(level++); child = parent) {
3306                            if (parent instanceof AST_Assign) {
3307                                if (parent.left === child) break;
3308                                if (parent.operator == "=") continue;
3309                                if (lazy_op[parent.operator.slice(0, -1)]) continue;
3310                                break;
3311                            }
3312                            if (parent instanceof AST_Binary) {
3313                                if (lazy_op[parent.operator]) continue;
3314                                break;
3315                            }
3316                            if (parent instanceof AST_Call) return;
3317                            if (parent instanceof AST_Scope) return;
3318                            if (parent instanceof AST_Sequence) {
3319                                if (parent.tail_node() === child) continue;
3320                                break;
3321                            }
3322                            if (parent instanceof AST_Template) {
3323                                if (parent.tag) return;
3324                                break;
3325                            }
3326                        }
3327                        node.enclosed.forEach(function(def) {
3328                            if (def.scope !== node) enclosed.set(def.name, true);
3329                        });
3330                        return true;
3331                    } else if (find_arguments && node instanceof AST_Sub) {
3332                        scope.each_argname(function(argname) {
3333                            if (!compressor.option("reduce_vars") || argname.definition().assignments) {
3334                                if (!argname.definition().fixed) well_defined = false;
3335                                lvalues.add(argname.name, true);
3336                            }
3337                        });
3338                        find_arguments = false;
3339                    }
3340                    if (!scan_toplevel) return;
3341                    if (node.TYPE == "Call") {
3342                        if (modify_toplevel) return;
3343                        var exp = node.expression;
3344                        if (exp instanceof AST_PropAccess) return;
3345                        if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return;
3346                        modify_toplevel = true;
3347                    } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
3348                        if (node === lhs && !(expr instanceof AST_Unary)) {
3349                            modify_toplevel = true;
3350                        } else {
3351                            read_toplevel = true;
3352                        }
3353                    }
3354                });
3355                expr.walk(tw);
3356                return lvalues;
3357            }
3358
3359            function remove_candidate(expr) {
3360                var value = rvalue === rhs_value ? null : make_sequence(rhs_value, rhs_value.expressions.slice(0, -1));
3361                var index = expr.name_index;
3362                if (index >= 0) {
3363                    var args, argname = scope.argnames[index];
3364                    if (argname instanceof AST_DefaultValue) {
3365                        scope.argnames[index] = argname = argname.clone();
3366                        argname.value = value || make_node(AST_Number, argname, { value: 0 });
3367                    } else if ((args = compressor.parent().args)[index]) {
3368                        scope.argnames[index] = argname.clone();
3369                        args[index] = value || make_node(AST_Number, args[index], { value: 0 });
3370                    }
3371                    return;
3372                }
3373                var end = hit_stack.length - 1;
3374                var last = hit_stack[end];
3375                if (last instanceof AST_VarDef || hit_stack[end - 1].body === last) end--;
3376                var tt = new TreeTransformer(function(node, descend, in_list) {
3377                    if (hit) return node;
3378                    if (node !== hit_stack[hit_index]) return node;
3379                    hit_index++;
3380                    if (hit_index <= end) return handle_custom_scan_order(node, tt);
3381                    hit = true;
3382                    if (node instanceof AST_Definitions) {
3383                        declare_only.set(last.name.name, (declare_only.get(last.name.name) || 0) + 1);
3384                        if (value_def) value_def.replaced++;
3385                        var defns = node.definitions;
3386                        var index = defns.indexOf(last);
3387                        var defn = last.clone();
3388                        defn.value = null;
3389                        if (!value) {
3390                            node.definitions[index] = defn;
3391                            return node;
3392                        }
3393                        var body = [ make_node(AST_SimpleStatement, value, { body: value }) ];
3394                        if (index > 0) {
3395                            var head = node.clone();
3396                            head.definitions = defns.slice(0, index);
3397                            body.unshift(head);
3398                            node = node.clone();
3399                            node.definitions = defns.slice(index);
3400                        }
3401                        body.push(node);
3402                        node.definitions[0] = defn;
3403                        return in_list ? List.splice(body) : make_node(AST_BlockStatement, node, { body: body });
3404                    }
3405                    if (!value) return in_list ? List.skip : null;
3406                    return is_statement(node) ? make_node(AST_SimpleStatement, value, { body: value }) : value;
3407                }, function(node, in_list) {
3408                    if (node instanceof AST_For) return patch_for_init(node, in_list);
3409                    return patch_sequence(node, tt);
3410                });
3411                abort = false;
3412                hit = false;
3413                hit_index = 0;
3414                if (!(statements[stat_index] = statements[stat_index].transform(tt))) statements.splice(stat_index, 1);
3415            }
3416
3417            function patch_sequence(node, tt) {
3418                if (node instanceof AST_Sequence) switch (node.expressions.length) {
3419                  case 0: return null;
3420                  case 1: return maintain_this_binding(tt.parent(), node, node.expressions[0]);
3421                }
3422            }
3423
3424            function is_lhs_local(lhs) {
3425                var sym = root_expr(lhs);
3426                if (!(sym instanceof AST_SymbolRef)) return false;
3427                if (sym.definition().scope.resolve() !== scope) return false;
3428                if (!in_loop) return true;
3429                if (compound) return false;
3430                if (candidate instanceof AST_Unary) return false;
3431                var lvalue = lvalues.get(sym.name);
3432                return !lvalue || lvalue[0] === lhs;
3433            }
3434
3435            function value_has_side_effects() {
3436                if (candidate instanceof AST_Unary) return false;
3437                return rvalue.has_side_effects(compressor);
3438            }
3439
3440            function replace_all_symbols(expr) {
3441                if (expr instanceof AST_Unary) return false;
3442                if (side_effects) return false;
3443                if (value_def) return true;
3444                if (!(lhs instanceof AST_SymbolRef)) return false;
3445                var referenced;
3446                if (expr instanceof AST_VarDef) {
3447                    referenced = 1;
3448                } else if (expr.operator == "=") {
3449                    referenced = 2;
3450                } else {
3451                    return false;
3452                }
3453                var def = lhs.definition();
3454                if (def.references.length - def.replaced == referenced) return true;
3455                if (!def.fixed) return false;
3456                if (!lhs.fixed) return false;
3457                var assigns = lhs.fixed.assigns;
3458                var matched = 0;
3459                if (!all(def.references, function(ref, index) {
3460                    var fixed = ref.fixed;
3461                    if (!fixed) return false;
3462                    if (fixed.to_binary || fixed.to_prefix) return false;
3463                    if (fixed === lhs.fixed) {
3464                        matched++;
3465                        return true;
3466                    }
3467                    return assigns && fixed.assigns && assigns[0] !== fixed.assigns[0];
3468                })) return false;
3469                if (matched != referenced) return false;
3470                verify_ref = true;
3471                return true;
3472            }
3473
3474            function symbol_in_lvalues(sym, parent) {
3475                var lvalue = lvalues.get(sym.name);
3476                if (!lvalue || all(lvalue, function(lhs) {
3477                    return !lhs;
3478                })) return;
3479                if (lvalue[0] !== lhs) return true;
3480                scan_rhs = false;
3481            }
3482
3483            function may_modify(sym) {
3484                var def = sym.definition();
3485                if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
3486                if (def.scope.resolve() !== scope) return true;
3487                if (modify_toplevel && compressor.exposed(def)) return true;
3488                return !all(def.references, function(ref) {
3489                    return ref.scope.resolve(true) === scope;
3490                });
3491            }
3492
3493            function side_effects_external(node, lhs) {
3494                if (node instanceof AST_Assign) return side_effects_external(node.left, true);
3495                if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
3496                if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
3497                if (lhs) {
3498                    if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
3499                    if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
3500                    if (node instanceof AST_SymbolRef) return node.definition().scope.resolve() !== scope;
3501                }
3502                return false;
3503            }
3504        }
3505
3506        function eliminate_spurious_blocks(statements) {
3507            var changed = false, seen_dirs = [];
3508            for (var i = 0; i < statements.length;) {
3509                var stat = statements[i];
3510                if (stat instanceof AST_BlockStatement) {
3511                    if (all(stat.body, safe_to_trim)) {
3512                        changed = true;
3513                        eliminate_spurious_blocks(stat.body);
3514                        [].splice.apply(statements, [i, 1].concat(stat.body));
3515                        i += stat.body.length;
3516                        continue;
3517                    }
3518                }
3519                if (stat instanceof AST_Directive) {
3520                    if (member(stat.value, seen_dirs)) {
3521                        changed = true;
3522                        statements.splice(i, 1);
3523                        continue;
3524                    }
3525                    seen_dirs.push(stat.value);
3526                }
3527                if (stat instanceof AST_EmptyStatement) {
3528                    changed = true;
3529                    statements.splice(i, 1);
3530                    continue;
3531                }
3532                i++;
3533            }
3534            return changed;
3535        }
3536
3537        function handle_if_return(statements, compressor) {
3538            var changed = false;
3539            var parent = compressor.parent();
3540            var self = compressor.self();
3541            var declare_only, jump, merge_jump;
3542            var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self;
3543            var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences");
3544            var drop_return_void = !(in_try && in_try.bfinally && in_async_generator(scope));
3545            var multiple_if_returns = has_multiple_if_returns(statements);
3546            for (var i = statements.length; --i >= 0;) {
3547                var stat = statements[i];
3548                var j = next_index(i);
3549                var next = statements[j];
3550
3551                if (in_lambda && declare_only && !next && stat instanceof AST_Return
3552                    && drop_return_void && !(self instanceof AST_SwitchBranch)) {
3553                    var body = stat.value;
3554                    if (!body) {
3555                        changed = true;
3556                        statements.splice(i, 1);
3557                        continue;
3558                    }
3559                    var tail = body.tail_node();
3560                    if (is_undefined(tail)) {
3561                        changed = true;
3562                        if (body instanceof AST_UnaryPrefix) {
3563                            body = body.expression;
3564                        } else if (tail instanceof AST_UnaryPrefix) {
3565                            body = body.clone();
3566                            body.expressions[body.expressions.length - 1] = tail.expression;
3567                        }
3568                        statements[i] = make_node(AST_SimpleStatement, stat, { body: body });
3569                        continue;
3570                    }
3571                }
3572
3573                if (stat instanceof AST_If) {
3574                    var ab = aborts(stat.body);
3575                    // if (foo()) { bar(); return; } else baz(); moo(); ---> if (foo()) bar(); else { baz(); moo(); }
3576                    if (can_merge_flow(ab)) {
3577                        if (ab.label) remove(ab.label.thedef.references, ab);
3578                        changed = true;
3579                        stat = stat.clone();
3580                        stat.body = make_node(AST_BlockStatement, stat, {
3581                            body: as_statement_array_with_return(stat.body, ab),
3582                        });
3583                        stat.alternative = make_node(AST_BlockStatement, stat, {
3584                            body: as_statement_array(stat.alternative).concat(extract_functions(merge_jump, jump)),
3585                        });
3586                        adjust_refs(ab.value, merge_jump);
3587                        statements[i] = stat;
3588                        statements[i] = stat.transform(compressor);
3589                        continue;
3590                    }
3591                    // if (foo()) { bar(); return x; } return y; ---> if (!foo()) return y; bar(); return x;
3592                    if (ab && !stat.alternative && next instanceof AST_Jump) {
3593                        var cond = stat.condition;
3594                        var preference = i + 1 == j && stat.body instanceof AST_BlockStatement;
3595                        cond = best_of_expression(cond, cond.negate(compressor), preference);
3596                        if (cond !== stat.condition) {
3597                            changed = true;
3598                            stat = stat.clone();
3599                            stat.condition = cond;
3600                            var body = stat.body;
3601                            stat.body = make_node(AST_BlockStatement, next, {
3602                                body: extract_functions(true, null, j + 1),
3603                            });
3604                            statements.splice(i, 1, stat, body);
3605                            // proceed further only if `TreeWalker.stack` is in a consistent state
3606                            //    https://github.com/mishoo/UglifyJS/issues/5595
3607                            //    https://github.com/mishoo/UglifyJS/issues/5597
3608                            if (!in_lambda || self instanceof AST_Block && self.body === statements) {
3609                                statements[i] = stat.transform(compressor);
3610                            }
3611                            continue;
3612                        }
3613                    }
3614                    var alt = aborts(stat.alternative);
3615                    // if (foo()) bar(); else { baz(); return; } moo(); ---> if (foo()) { bar(); moo(); } else baz();
3616                    if (can_merge_flow(alt)) {
3617                        if (alt.label) remove(alt.label.thedef.references, alt);
3618                        changed = true;
3619                        stat = stat.clone();
3620                        stat.body = make_node(AST_BlockStatement, stat.body, {
3621                            body: as_statement_array(stat.body).concat(extract_functions(merge_jump, jump)),
3622                        });
3623                        stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
3624                            body: as_statement_array_with_return(stat.alternative, alt),
3625                        });
3626                        adjust_refs(alt.value, merge_jump);
3627                        statements[i] = stat;
3628                        statements[i] = stat.transform(compressor);
3629                        continue;
3630                    }
3631                    if (compressor.option("typeofs")) {
3632                        if (ab && !alt) {
3633                            var stats = make_node(AST_BlockStatement, self, { body: statements.slice(i + 1) });
3634                            mark_locally_defined(stat.condition, null, stats);
3635                        }
3636                        if (!ab && alt) {
3637                            var stats = make_node(AST_BlockStatement, self, { body: statements.slice(i + 1) });
3638                            mark_locally_defined(stat.condition, stats);
3639                        }
3640                    }
3641                }
3642
3643                if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3644                    var value = stat.body.value;
3645                    var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
3646                    // if (foo()) return x; return y; ---> return foo() ? x : y;
3647                    if (!stat.alternative && next instanceof AST_Return
3648                        && (drop_return_void || !value == !next.value)) {
3649                        changed = true;
3650                        stat = stat.clone();
3651                        stat.alternative = make_node(AST_BlockStatement, next, {
3652                            body: extract_functions(true, null, j + 1),
3653                        });
3654                        statements[i] = stat;
3655                        statements[i] = stat.transform(compressor);
3656                        continue;
3657                    }
3658                    // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined;
3659                    // if (foo()) return bar() ? x : void 0; ---> return foo() && bar() ? x : void 0;
3660                    // if (foo()) return bar() ? void 0 : x; ---> return !foo() || bar() ? void 0 : x;
3661                    if (in_lambda && declare_only && !next && !stat.alternative && (in_bool
3662                        || value && multiple_if_returns
3663                        || value instanceof AST_Conditional && (is_undefined(value.consequent, compressor)
3664                            || is_undefined(value.alternative, compressor)))) {
3665                        changed = true;
3666                        stat = stat.clone();
3667                        stat.alternative = make_node(AST_Return, stat, { value: null });
3668                        statements[i] = stat;
3669                        statements[i] = stat.transform(compressor);
3670                        continue;
3671                    }
3672                    // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e;
3673                    //
3674                    // if sequences is not enabled, this can lead to an endless loop (issue #866).
3675                    // however, with sequences on this helps producing slightly better output for
3676                    // the example code.
3677                    var prev, prev_stat;
3678                    if (chain_if_returns && !stat.alternative
3679                        && (!(prev_stat = statements[prev = prev_index(i)]) && in_iife
3680                            || prev_stat instanceof AST_If && prev_stat.body instanceof AST_Return)
3681                        && (!next ? !declare_only
3682                            : next instanceof AST_SimpleStatement && next_index(j) == statements.length)) {
3683                        changed = true;
3684                        var exprs = [];
3685                        stat = stat.clone();
3686                        exprs.push(stat.condition);
3687                        stat.condition = make_sequence(stat, exprs);
3688                        stat.alternative = make_node(AST_BlockStatement, self, {
3689                            body: extract_functions().concat(make_node(AST_Return, self, { value: null })),
3690                        });
3691                        statements[i] = stat.transform(compressor);
3692                        i = prev + 1;
3693                        continue;
3694                    }
3695                }
3696
3697                if (stat instanceof AST_Break || stat instanceof AST_Exit) {
3698                    jump = stat;
3699                    continue;
3700                }
3701
3702                if (declare_only && jump && jump === next) eliminate_returns(stat);
3703            }
3704            return changed;
3705
3706            function has_multiple_if_returns(statements) {
3707                var n = 0;
3708                for (var i = statements.length; --i >= 0;) {
3709                    var stat = statements[i];
3710                    if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3711                        if (++n > 1) return true;
3712                    }
3713                }
3714                return false;
3715            }
3716
3717            function match_target(target) {
3718                return last_of(compressor, function(node) {
3719                    return node === target;
3720                });
3721            }
3722
3723            function match_return(ab, exact) {
3724                if (!jump) return false;
3725                if (jump.TYPE != ab.TYPE) return false;
3726                var value = ab.value;
3727                if (!value) return false;
3728                var equals = jump.equals(ab);
3729                if (!equals && value instanceof AST_Sequence) {
3730                    value = value.tail_node();
3731                    if (jump.value && jump.value.equals(value)) equals = 2;
3732                }
3733                if (!equals && !exact && jump.value instanceof AST_Sequence) {
3734                    if (jump.value.tail_node().equals(value)) equals = 3;
3735                }
3736                return equals;
3737            }
3738
3739            function can_drop_abort(ab) {
3740                if (ab instanceof AST_Exit) {
3741                    if (merge_jump = match_return(ab)) return true;
3742                    if (!in_lambda) return false;
3743                    if (!(ab instanceof AST_Return)) return false;
3744                    var value = ab.value;
3745                    if (value && !is_undefined(value.tail_node())) return false;
3746                    if (!(self instanceof AST_SwitchBranch)) return true;
3747                    if (!jump) return false;
3748                    if (jump instanceof AST_Exit && jump.value) return false;
3749                    merge_jump = 4;
3750                    return true;
3751                }
3752                if (!(ab instanceof AST_LoopControl)) return false;
3753                if (self instanceof AST_SwitchBranch) {
3754                    if (jump instanceof AST_Exit) {
3755                        if (!in_lambda) return false;
3756                        if (jump.value) return false;
3757                        merge_jump = true;
3758                    } else if (jump) {
3759                        if (compressor.loopcontrol_target(jump) !== parent) return false;
3760                        merge_jump = true;
3761                    } else if (jump === false) {
3762                        return false;
3763                    }
3764                }
3765                var lct = compressor.loopcontrol_target(ab);
3766                if (ab instanceof AST_Continue) return match_target(loop_body(lct));
3767                if (lct instanceof AST_IterationStatement) return false;
3768                return match_target(lct);
3769            }
3770
3771            function can_merge_flow(ab) {
3772                merge_jump = false;
3773                if (!can_drop_abort(ab)) return false;
3774                for (var j = statements.length; --j > i;) {
3775                    var stat = statements[j];
3776                    if (stat instanceof AST_DefClass) {
3777                        if (stat.name.definition().preinit) return false;
3778                    } else if (stat instanceof AST_Const || stat instanceof AST_Let) {
3779                        if (!all(stat.definitions, function(defn) {
3780                            return !defn.name.match_symbol(function(node) {
3781                                return node instanceof AST_SymbolDeclaration && node.definition().preinit;
3782                            });
3783                        })) return false;
3784                    }
3785                }
3786                return true;
3787            }
3788
3789            function extract_functions(mode, stop, end) {
3790                var defuns = [];
3791                var lexical = false;
3792                var start = i + 1;
3793                if (!mode) {
3794                    end = statements.length;
3795                    jump = null;
3796                } else if (stop) {
3797                    end = statements.lastIndexOf(stop);
3798                } else {
3799                    stop = statements[end];
3800                    if (stop !== jump) jump = false;
3801                }
3802                var tail = statements.splice(start, end - start).filter(function(stat) {
3803                    if (stat instanceof AST_LambdaDefinition) {
3804                        defuns.push(stat);
3805                        return false;
3806                    }
3807                    if (is_lexical_definition(stat)) lexical = true;
3808                    return true;
3809                });
3810                if (mode === 3) {
3811                    tail.push(make_node(AST_SimpleStatement, stop.value, {
3812                        body: make_sequence(stop.value, stop.value.expressions.slice(0, -1)),
3813                    }));
3814                    stop.value = stop.value.tail_node();
3815                }
3816                [].push.apply(lexical ? tail : statements, defuns);
3817                return tail;
3818            }
3819
3820            function trim_return(value, mode) {
3821                if (value) switch (mode) {
3822                  case 4:
3823                    return value;
3824                  case 3:
3825                    if (!(value instanceof AST_Sequence)) break;
3826                  case 2:
3827                    return make_sequence(value, value.expressions.slice(0, -1));
3828                }
3829            }
3830
3831            function as_statement_array_with_return(node, ab) {
3832                var body = as_statement_array(node);
3833                var block = body, last;
3834                while ((last = block[block.length - 1]) !== ab) {
3835                    block = last.body;
3836                }
3837                block.pop();
3838                var value = ab.value;
3839                if (merge_jump) value = trim_return(value, merge_jump);
3840                if (value) block.push(make_node(AST_SimpleStatement, value, { body: value }));
3841                return body;
3842            }
3843
3844            function adjust_refs(value, mode) {
3845                if (!mode) return;
3846                if (!value) return;
3847                switch (mode) {
3848                  case 4:
3849                    return;
3850                  case 3:
3851                  case 2:
3852                    value = value.tail_node();
3853                }
3854                merge_expression(value, jump.value);
3855            }
3856
3857            function next_index(i) {
3858                declare_only = true;
3859                for (var j = i; ++j < statements.length;) {
3860                    var stat = statements[j];
3861                    if (is_declaration(stat)) continue;
3862                    if (stat instanceof AST_Var) {
3863                        declare_only = false;
3864                        continue;
3865                    }
3866                    break;
3867                }
3868                return j;
3869            }
3870
3871            function prev_index(i) {
3872                for (var j = i; --j >= 0;) {
3873                    var stat = statements[j];
3874                    if (stat instanceof AST_Var) continue;
3875                    if (is_declaration(stat)) continue;
3876                    break;
3877                }
3878                return j;
3879            }
3880
3881            function eliminate_returns(stat, keep_throws, in_block) {
3882                if (stat instanceof AST_Exit) {
3883                    var mode = !(keep_throws && stat instanceof AST_Throw) && match_return(stat, true);
3884                    if (mode) {
3885                        changed = true;
3886                        var value = trim_return(stat.value, mode);
3887                        if (value) return make_node(AST_SimpleStatement, value, { body: value });
3888                        return in_block ? null : make_node(AST_EmptyStatement, stat);
3889                    }
3890                } else if (stat instanceof AST_If) {
3891                    stat.body = eliminate_returns(stat.body, keep_throws);
3892                    if (stat.alternative) stat.alternative = eliminate_returns(stat.alternative, keep_throws);
3893                } else if (stat instanceof AST_LabeledStatement) {
3894                    stat.body = eliminate_returns(stat.body, keep_throws);
3895                } else if (stat instanceof AST_Try) {
3896                    if (!stat.bfinally || !jump.value || jump.value.is_constant()) {
3897                        if (stat.bcatch) eliminate_returns(stat.bcatch, keep_throws);
3898                        var trimmed = eliminate_returns(stat.body.pop(), true, true);
3899                        if (trimmed) stat.body.push(trimmed);
3900                    }
3901                } else if (stat instanceof AST_Block && !(stat instanceof AST_Scope || stat instanceof AST_Switch)) {
3902                    var trimmed = eliminate_returns(stat.body.pop(), keep_throws, true);
3903                    if (trimmed) stat.body.push(trimmed);
3904                }
3905                return stat;
3906            }
3907        }
3908
3909        function eliminate_dead_code(statements, compressor) {
3910            var has_quit;
3911            var self = compressor.self();
3912            if (self instanceof AST_Catch) {
3913                self = compressor.parent();
3914            } else if (self instanceof AST_LabeledStatement) {
3915                self = self.body;
3916            }
3917            for (var i = 0, n = 0, len = statements.length; i < len; i++) {
3918                var stat = statements[i];
3919                if (stat instanceof AST_LoopControl) {
3920                    var lct = compressor.loopcontrol_target(stat);
3921                    if (loop_body(lct) !== self
3922                        || stat instanceof AST_Break && lct instanceof AST_IterationStatement) {
3923                        statements[n++] = stat;
3924                    } else if (stat.label) {
3925                        remove(stat.label.thedef.references, stat);
3926                    }
3927                } else {
3928                    statements[n++] = stat;
3929                }
3930                if (aborts(stat)) {
3931                    has_quit = statements.slice(i + 1);
3932                    break;
3933                }
3934            }
3935            statements.length = n;
3936            if (has_quit) has_quit.forEach(function(stat) {
3937                extract_declarations_from_unreachable_code(compressor, stat, statements);
3938            });
3939            return statements.length != len;
3940        }
3941
3942        function trim_awaits(statements, compressor) {
3943            if (!in_lambda || in_try && in_try.bfinally) return;
3944            var changed = false;
3945            for (var index = statements.length; --index >= 0;) {
3946                var stat = statements[index];
3947                if (!(stat instanceof AST_SimpleStatement)) break;
3948                var node = stat.body;
3949                if (!(node instanceof AST_Await)) break;
3950                var exp = node.expression;
3951                if (!needs_enqueuing(compressor, exp)) break;
3952                changed = true;
3953                exp = exp.drop_side_effect_free(compressor, true);
3954                if (exp) {
3955                    stat.body = exp;
3956                    break;
3957                }
3958            }
3959            statements.length = index + 1;
3960            return changed;
3961        }
3962
3963        function inline_iife(statements, compressor) {
3964            var changed = false;
3965            var index = statements.length - 1;
3966            if (in_lambda && index >= 0) {
3967                var no_return = in_try && in_try.bfinally && in_async_generator(scope);
3968                var inlined = statements[index].try_inline(compressor, block_scope, no_return);
3969                if (inlined) {
3970                    statements[index--] = inlined;
3971                    changed = true;
3972                }
3973            }
3974            var loop = in_loop && in_try && in_try.bfinally ? "try" : in_loop;
3975            for (; index >= 0; index--) {
3976                var inlined = statements[index].try_inline(compressor, block_scope, true, loop);
3977                if (!inlined) continue;
3978                statements[index] = inlined;
3979                changed = true;
3980            }
3981            return changed;
3982        }
3983
3984        function sequencesize(statements, compressor) {
3985            if (statements.length < 2) return;
3986            var seq = [], n = 0;
3987            function push_seq() {
3988                if (!seq.length) return;
3989                var body = make_sequence(seq[0], seq);
3990                statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
3991                seq = [];
3992            }
3993            for (var i = 0, len = statements.length; i < len; i++) {
3994                var stat = statements[i];
3995                if (stat instanceof AST_SimpleStatement) {
3996                    if (seq.length >= compressor.sequences_limit) push_seq();
3997                    merge_sequence(seq, stat.body);
3998                } else if (is_declaration(stat)) {
3999                    statements[n++] = stat;
4000                } else {
4001                    push_seq();
4002                    statements[n++] = stat;
4003                }
4004            }
4005            push_seq();
4006            statements.length = n;
4007            return n != len;
4008        }
4009
4010        function to_simple_statement(block, decls) {
4011            if (!(block instanceof AST_BlockStatement)) return block;
4012            var stat = null;
4013            for (var i = 0; i < block.body.length; i++) {
4014                var line = block.body[i];
4015                if (line instanceof AST_Var && declarations_only(line)) {
4016                    decls.push(line);
4017                } else if (stat || is_lexical_definition(line)) {
4018                    return false;
4019                } else {
4020                    stat = line;
4021                }
4022            }
4023            return stat;
4024        }
4025
4026        function sequencesize_2(statements, compressor) {
4027            var changed = false, n = 0, prev;
4028            for (var i = 0; i < statements.length; i++) {
4029                var stat = statements[i];
4030                if (prev) {
4031                    if (stat instanceof AST_Exit) {
4032                        if (stat.value || !in_async_generator(scope)) {
4033                            stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).optimize(compressor);
4034                        }
4035                    } else if (stat instanceof AST_For) {
4036                        if (!(stat.init instanceof AST_Definitions)) {
4037                            var abort = false;
4038                            prev.body.walk(new TreeWalker(function(node) {
4039                                if (abort || node instanceof AST_Scope) return true;
4040                                if (node instanceof AST_Binary && node.operator == "in") {
4041                                    abort = true;
4042                                    return true;
4043                                }
4044                            }));
4045                            if (!abort) {
4046                                if (stat.init) stat.init = cons_seq(stat.init);
4047                                else {
4048                                    stat.init = prev.body;
4049                                    n--;
4050                                    changed = true;
4051                                }
4052                            }
4053                        }
4054                    } else if (stat instanceof AST_ForIn) {
4055                        if (!is_lexical_definition(stat.init)) stat.object = cons_seq(stat.object);
4056                    } else if (stat instanceof AST_If) {
4057                        stat.condition = cons_seq(stat.condition);
4058                    } else if (stat instanceof AST_Switch) {
4059                        stat.expression = cons_seq(stat.expression);
4060                    } else if (stat instanceof AST_With) {
4061                        stat.expression = cons_seq(stat.expression);
4062                    }
4063                }
4064                if (compressor.option("conditionals") && stat instanceof AST_If) {
4065                    var decls = [];
4066                    var body = to_simple_statement(stat.body, decls);
4067                    var alt = to_simple_statement(stat.alternative, decls);
4068                    if (body !== false && alt !== false && decls.length > 0) {
4069                        var len = decls.length;
4070                        decls.push(make_node(AST_If, stat, {
4071                            condition: stat.condition,
4072                            body: body || make_node(AST_EmptyStatement, stat.body),
4073                            alternative: alt,
4074                        }));
4075                        decls.unshift(n, 1);
4076                        [].splice.apply(statements, decls);
4077                        i += len;
4078                        n += len + 1;
4079                        prev = null;
4080                        changed = true;
4081                        continue;
4082                    }
4083                }
4084                statements[n++] = stat;
4085                prev = stat instanceof AST_SimpleStatement ? stat : null;
4086            }
4087            statements.length = n;
4088            return changed;
4089
4090            function cons_seq(right) {
4091                n--;
4092                changed = true;
4093                var left = prev.body;
4094                return make_sequence(left, [ left, right ]);
4095            }
4096        }
4097
4098        function extract_exprs(body) {
4099            if (body instanceof AST_Assign) return [ body ];
4100            if (body instanceof AST_Sequence) return body.expressions.slice();
4101        }
4102
4103        function join_assigns(defn, body, keep) {
4104            var exprs = extract_exprs(body);
4105            if (!exprs) return;
4106            keep = keep || 0;
4107            var trimmed = false;
4108            for (var i = exprs.length - keep; --i >= 0;) {
4109                var expr = exprs[i];
4110                if (!can_trim(expr)) continue;
4111                var tail;
4112                if (expr.left instanceof AST_SymbolRef) {
4113                    tail = exprs.slice(i + 1);
4114                } else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) {
4115                    tail = exprs.slice(i + 1);
4116                    var flattened = expr.clone();
4117                    expr = expr.left.expression;
4118                    flattened.left = flattened.left.clone();
4119                    flattened.left.expression = expr.left.clone();
4120                    tail.unshift(flattened);
4121                } else {
4122                    continue;
4123                }
4124                if (tail.length == 0) continue;
4125                if (!trim_assigns(expr.left, expr.right, tail)) continue;
4126                trimmed = true;
4127                exprs = exprs.slice(0, i).concat(expr, tail);
4128            }
4129            if (defn instanceof AST_Definitions) {
4130                for (var i = defn.definitions.length; --i >= 0;) {
4131                    var def = defn.definitions[i];
4132                    if (!def.value) continue;
4133                    if (trim_assigns(def.name, def.value, exprs)) trimmed = true;
4134                    if (merge_conditional_assignments(def, exprs, keep)) trimmed = true;
4135                    break;
4136                }
4137                if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
4138            }
4139            return trimmed && exprs;
4140
4141            function can_trim(node) {
4142                return node instanceof AST_Assign && node.operator == "=";
4143            }
4144        }
4145
4146        function merge_assigns(prev, defn) {
4147            if (!(prev instanceof AST_SimpleStatement)) return;
4148            if (declarations_only(defn)) return;
4149            var exprs = extract_exprs(prev.body);
4150            if (!exprs) return;
4151            var definitions = [];
4152            if (!join_var_assign(definitions, exprs.reverse(), 0)) return;
4153            defn.definitions = definitions.reverse().concat(defn.definitions);
4154            return exprs.reverse();
4155        }
4156
4157        function merge_conditional_assignments(var_def, exprs, keep) {
4158            if (!compressor.option("conditionals")) return;
4159            if (var_def.name instanceof AST_Destructured) return;
4160            var trimmed = false;
4161            var def = var_def.name.definition();
4162            while (exprs.length > keep) {
4163                var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]);
4164                if (!cond) break;
4165                var_def.value = cond;
4166                exprs.shift();
4167                trimmed = true;
4168            }
4169            return trimmed;
4170        }
4171
4172        function join_var_assign(definitions, exprs, keep) {
4173            var trimmed = false;
4174            while (exprs.length > keep) {
4175                var expr = exprs[0];
4176                if (!(expr instanceof AST_Assign)) break;
4177                if (expr.operator != "=") break;
4178                var lhs = expr.left;
4179                if (!(lhs instanceof AST_SymbolRef)) break;
4180                if (is_undeclared_ref(lhs)) break;
4181                if (lhs.scope.resolve() !== scope) break;
4182                var def = lhs.definition();
4183                if (def.scope !== scope) break;
4184                if (def.orig.length > def.eliminated + 1) break;
4185                if (def.orig[0].TYPE != "SymbolVar") break;
4186                var name = make_node(AST_SymbolVar, lhs);
4187                definitions.push(make_node(AST_VarDef, expr, {
4188                    name: name,
4189                    value: expr.right,
4190                }));
4191                def.orig.push(name);
4192                def.replaced++;
4193                exprs.shift();
4194                trimmed = true;
4195            }
4196            return trimmed;
4197        }
4198
4199        function trim_assigns(name, value, exprs) {
4200            var names = new Dictionary();
4201            names.set(name.name, true);
4202            while (value instanceof AST_Assign && value.operator == "=") {
4203                if (value.left instanceof AST_SymbolRef) names.set(value.left.name, true);
4204                value = value.right;
4205            }
4206            if (!(value instanceof AST_Object)) return;
4207            var trimmed = false;
4208            do {
4209                if (!try_join(exprs[0])) break;
4210                exprs.shift();
4211                trimmed = true;
4212            } while (exprs.length);
4213            return trimmed;
4214
4215            function try_join(node) {
4216                if (!(node instanceof AST_Assign)) return;
4217                if (node.operator != "=") return;
4218                if (!(node.left instanceof AST_PropAccess)) return;
4219                var sym = node.left.expression;
4220                if (!(sym instanceof AST_SymbolRef)) return;
4221                if (!names.has(sym.name)) return;
4222                if (!node.right.is_constant_expression(scope)) return;
4223                var prop = node.left.property;
4224                if (prop instanceof AST_Node) {
4225                    if (try_join(prop)) prop = node.left.property = prop.right.clone();
4226                    prop = prop.evaluate(compressor);
4227                }
4228                if (prop instanceof AST_Node) return;
4229                prop = "" + prop;
4230                var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
4231                    var key = node.key;
4232                    return typeof key == "string" && key != prop && key != "__proto__";
4233                } : function(node) {
4234                    var key = node.key;
4235                    if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) {
4236                        return typeof key == "string" && key != prop;
4237                    }
4238                    return key !== "__proto__";
4239                };
4240                if (!all(value.properties, diff)) return;
4241                value.properties.push(make_node(AST_ObjectKeyVal, node, {
4242                    key: prop,
4243                    value: node.right,
4244                }));
4245                return true;
4246            }
4247        }
4248
4249        function join_consecutive_vars(statements) {
4250            var changed = false, defs;
4251            for (var i = 0, j = -1; i < statements.length; i++) {
4252                var stat = statements[i];
4253                var prev = statements[j];
4254                if (stat instanceof AST_Definitions) {
4255                    if (prev && prev.TYPE == stat.TYPE) {
4256                        prev.definitions = prev.definitions.concat(stat.definitions);
4257                        changed = true;
4258                    } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
4259                        defs.definitions = defs.definitions.concat(stat.definitions);
4260                        changed = true;
4261                    } else if (stat instanceof AST_Var) {
4262                        var exprs = merge_assigns(prev, stat);
4263                        if (exprs) {
4264                            if (exprs.length) {
4265                                prev.body = make_sequence(prev, exprs);
4266                                j++;
4267                            }
4268                            changed = true;
4269                        } else {
4270                            j++;
4271                        }
4272                        statements[j] = defs = stat;
4273                    } else {
4274                        statements[++j] = stat;
4275                    }
4276                    continue;
4277                } else if (stat instanceof AST_Exit) {
4278                    stat.value = join_assigns_expr(stat.value);
4279                } else if (stat instanceof AST_For) {
4280                    var exprs = join_assigns(prev, stat.init);
4281                    if (exprs) {
4282                        changed = true;
4283                        stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
4284                    } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
4285                        if (stat.init) {
4286                            prev.definitions = prev.definitions.concat(stat.init.definitions);
4287                        }
4288                        stat = stat.clone();
4289                        defs = stat.init = prev;
4290                        statements[j] = merge_defns(stat);
4291                        changed = true;
4292                        continue;
4293                    } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
4294                        defs.definitions = defs.definitions.concat(stat.init.definitions);
4295                        stat.init = null;
4296                        changed = true;
4297                    } else if (stat.init instanceof AST_Var) {
4298                        defs = stat.init;
4299                        exprs = merge_assigns(prev, stat.init);
4300                        if (exprs) {
4301                            changed = true;
4302                            if (exprs.length == 0) {
4303                                statements[j] = merge_defns(stat);
4304                                continue;
4305                            }
4306                            prev.body = make_sequence(prev, exprs);
4307                        }
4308                    }
4309                } else if (stat instanceof AST_ForEnumeration) {
4310                    if (defs && defs.TYPE == stat.init.TYPE) {
4311                        var defns = defs.definitions.slice();
4312                        stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) {
4313                            defns.push(make_node(AST_VarDef, name, {
4314                                name: name,
4315                                value: null,
4316                            }));
4317                            name.definition().references.push(ref);
4318                        });
4319                        defs.definitions = defns;
4320                        changed = true;
4321                    }
4322                    stat.object = join_assigns_expr(stat.object);
4323                } else if (stat instanceof AST_If) {
4324                    stat.condition = join_assigns_expr(stat.condition);
4325                } else if (stat instanceof AST_SimpleStatement) {
4326                    var exprs = join_assigns(prev, stat.body), next;
4327                    if (exprs) {
4328                        changed = true;
4329                        if (!exprs.length) continue;
4330                        stat.body = make_sequence(stat.body, exprs);
4331                    } else if (prev instanceof AST_Definitions
4332                        && (next = statements[i + 1])
4333                        && prev.TYPE == next.TYPE
4334                        && (next = next.definitions[0]).value) {
4335                        changed = true;
4336                        next.value = make_sequence(stat, [ stat.body, next.value ]);
4337                        continue;
4338                    }
4339                } else if (stat instanceof AST_Switch) {
4340                    stat.expression = join_assigns_expr(stat.expression);
4341                } else if (stat instanceof AST_With) {
4342                    stat.expression = join_assigns_expr(stat.expression);
4343                }
4344                statements[++j] = defs ? merge_defns(stat) : stat;
4345            }
4346            statements.length = j + 1;
4347            return changed;
4348
4349            function join_assigns_expr(value) {
4350                var exprs = join_assigns(prev, value, 1);
4351                if (!exprs) return value;
4352                changed = true;
4353                var tail = value.tail_node();
4354                if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left);
4355                return make_sequence(value, exprs);
4356            }
4357
4358            function merge_defns(stat) {
4359                return stat.transform(new TreeTransformer(function(node, descend, in_list) {
4360                    if (node instanceof AST_Definitions) {
4361                        if (defs === node) return node;
4362                        if (defs.TYPE != node.TYPE) return node;
4363                        var parent = this.parent();
4364                        if (parent instanceof AST_ForEnumeration && parent.init === node) return node;
4365                        if (!declarations_only(node)) return node;
4366                        defs.definitions = defs.definitions.concat(node.definitions);
4367                        changed = true;
4368                        if (parent instanceof AST_For && parent.init === node) return null;
4369                        return in_list ? List.skip : make_node(AST_EmptyStatement, node);
4370                    }
4371                    if (node instanceof AST_ExportDeclaration) return node;
4372                    if (node instanceof AST_Scope) return node;
4373                    if (!is_statement(node)) return node;
4374                }));
4375            }
4376        }
4377    }
4378
4379    function extract_declarations_from_unreachable_code(compressor, stat, target) {
4380        var block;
4381        var dropped = false;
4382        stat.walk(new TreeWalker(function(node, descend) {
4383            if (node instanceof AST_DefClass) {
4384                node.extends = null;
4385                node.properties = [];
4386                push(node);
4387                return true;
4388            }
4389            if (node instanceof AST_Definitions) {
4390                var defns = [];
4391                if (node.remove_initializers(compressor, defns)) {
4392                    AST_Node.warn("Dropping initialization in unreachable code [{start}]", node);
4393                }
4394                if (defns.length > 0) {
4395                    node.definitions = defns;
4396                    push(node);
4397                }
4398                return true;
4399            }
4400            if (node instanceof AST_LambdaDefinition) {
4401                push(node);
4402                return true;
4403            }
4404            if (node instanceof AST_Scope) return true;
4405            if (node instanceof AST_BlockScope) {
4406                var save = block;
4407                block = [];
4408                descend();
4409                if (block.required) {
4410                    target.push(make_node(AST_BlockStatement, stat, { body: block }));
4411                } else if (block.length) {
4412                    [].push.apply(target, block);
4413                }
4414                block = save;
4415                return true;
4416            }
4417            if (!(node instanceof AST_LoopControl)) dropped = true;
4418        }));
4419        if (dropped) AST_Node.warn("Dropping unreachable code [{start}]", stat);
4420
4421        function push(node) {
4422            if (block) {
4423                block.push(node);
4424                if (!safe_to_trim(node)) block.required = true;
4425            } else {
4426                target.push(node);
4427            }
4428        }
4429    }
4430
4431    function is_undefined(node, compressor) {
4432        return node == null
4433            || node.is_undefined
4434            || node instanceof AST_Undefined
4435            || node instanceof AST_UnaryPrefix
4436                && node.operator == "void"
4437                && !(compressor && node.expression.has_side_effects(compressor));
4438    }
4439
4440    // in_strict_mode()
4441    // return true if scope executes in Strict Mode
4442    (function(def) {
4443        def(AST_Class, return_true);
4444        def(AST_Scope, function(compressor) {
4445            var body = this.body;
4446            for (var i = 0; i < body.length; i++) {
4447                var stat = body[i];
4448                if (!(stat instanceof AST_Directive)) break;
4449                if (stat.value == "use strict") return true;
4450            }
4451            var parent = this.parent_scope;
4452            if (!parent) return compressor.option("module");
4453            return parent.resolve(true).in_strict_mode(compressor);
4454        });
4455    })(function(node, func) {
4456        node.DEFMETHOD("in_strict_mode", func);
4457    });
4458
4459    // is_truthy()
4460    // return true if `!!node === true`
4461    (function(def) {
4462        def(AST_Node, return_false);
4463        def(AST_Array, return_true);
4464        def(AST_Assign, function() {
4465            return this.operator == "=" && this.right.is_truthy();
4466        });
4467        def(AST_Lambda, return_true);
4468        def(AST_Object, return_true);
4469        def(AST_RegExp, return_true);
4470        def(AST_Sequence, function() {
4471            return this.tail_node().is_truthy();
4472        });
4473        def(AST_SymbolRef, function() {
4474            var fixed = this.fixed_value();
4475            if (!fixed) return false;
4476            this.is_truthy = return_false;
4477            var result = fixed.is_truthy();
4478            delete this.is_truthy;
4479            return result;
4480        });
4481    })(function(node, func) {
4482        node.DEFMETHOD("is_truthy", func);
4483    });
4484
4485    // is_negative_zero()
4486    // return true if the node may represent -0
4487    (function(def) {
4488        def(AST_Node, return_true);
4489        def(AST_Array, return_false);
4490        function binary(op, left, right) {
4491            switch (op) {
4492              case "-":
4493                return left.is_negative_zero()
4494                    && (!(right instanceof AST_Constant) || right.value == 0);
4495              case "&&":
4496              case "||":
4497                return left.is_negative_zero() || right.is_negative_zero();
4498              case "*":
4499              case "/":
4500              case "%":
4501              case "**":
4502                return true;
4503              default:
4504                return false;
4505            }
4506        }
4507        def(AST_Assign, function() {
4508            var op = this.operator;
4509            if (op == "=") return this.right.is_negative_zero();
4510            return binary(op.slice(0, -1), this.left, this.right);
4511        });
4512        def(AST_Binary, function() {
4513            return binary(this.operator, this.left, this.right);
4514        });
4515        def(AST_Constant, function() {
4516            return this.value == 0 && 1 / this.value < 0;
4517        });
4518        def(AST_Lambda, return_false);
4519        def(AST_Object, return_false);
4520        def(AST_RegExp, return_false);
4521        def(AST_Sequence, function() {
4522            return this.tail_node().is_negative_zero();
4523        });
4524        def(AST_SymbolRef, function() {
4525            var fixed = this.fixed_value();
4526            if (!fixed) return true;
4527            this.is_negative_zero = return_true;
4528            var result = fixed.is_negative_zero();
4529            delete this.is_negative_zero;
4530            return result;
4531        });
4532        def(AST_UnaryPrefix, function() {
4533            return this.operator == "+" && this.expression.is_negative_zero()
4534                || this.operator == "-";
4535        });
4536    })(function(node, func) {
4537        node.DEFMETHOD("is_negative_zero", func);
4538    });
4539
4540    // may_throw_on_access()
4541    // returns true if this node may be null, undefined or contain `AST_Accessor`
4542    (function(def) {
4543        AST_Node.DEFMETHOD("may_throw_on_access", function(compressor, force) {
4544            return !compressor.option("pure_getters") || this._dot_throw(compressor, force);
4545        });
4546        function is_strict(compressor, force) {
4547            return force || /strict/.test(compressor.option("pure_getters"));
4548        }
4549        def(AST_Node, is_strict);
4550        def(AST_Array, return_false);
4551        def(AST_Assign, function(compressor) {
4552            var op = this.operator;
4553            var sym = this.left;
4554            var rhs = this.right;
4555            if (op != "=") {
4556                return lazy_op[op.slice(0, -1)] && (sym._dot_throw(compressor) || rhs._dot_throw(compressor));
4557            }
4558            if (!rhs._dot_throw(compressor)) return false;
4559            if (!(sym instanceof AST_SymbolRef)) return true;
4560            if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
4561                return rhs.right._dot_throw(compressor);
4562            }
4563            return true;
4564        });
4565        def(AST_Binary, function(compressor) {
4566            return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
4567        });
4568        def(AST_Class, function(compressor, force) {
4569            return is_strict(compressor, force) && !all(this.properties, function(prop) {
4570                if (prop.private) return true;
4571                if (!prop.static) return true;
4572                return !(prop instanceof AST_ClassGetter || prop instanceof AST_ClassSetter);
4573            });
4574        });
4575        def(AST_Conditional, function(compressor) {
4576            return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
4577        });
4578        def(AST_Constant, return_false);
4579        def(AST_Dot, function(compressor, force) {
4580            if (!is_strict(compressor, force)) return false;
4581            var exp = this.expression;
4582            if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
4583            return !(this.property == "prototype" && is_lambda(exp));
4584        });
4585        def(AST_Lambda, return_false);
4586        def(AST_Null, return_true);
4587        def(AST_Object, function(compressor, force) {
4588            return is_strict(compressor, force) && !all(this.properties, function(prop) {
4589                if (prop instanceof AST_ObjectGetter || prop instanceof AST_ObjectSetter) return false;
4590                return !(prop.key === "__proto__" && prop.value._dot_throw(compressor, force));
4591            });
4592        });
4593        def(AST_ObjectIdentity, function(compressor, force) {
4594            return is_strict(compressor, force) && !this.scope.resolve().new;
4595        });
4596        def(AST_Sequence, function(compressor) {
4597            return this.tail_node()._dot_throw(compressor);
4598        });
4599        def(AST_SymbolRef, function(compressor, force) {
4600            if (this.is_undefined) return true;
4601            if (!is_strict(compressor, force)) return false;
4602            if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
4603            if (this.is_immutable()) return false;
4604            var def = this.definition();
4605            if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) {
4606                return argname instanceof AST_SymbolFunarg;
4607            })) return def.scope.uses_arguments > 2;
4608            var fixed = this.fixed_value(true);
4609            if (!fixed) return true;
4610            this._dot_throw = return_true;
4611            if (fixed._dot_throw(compressor)) {
4612                delete this._dot_throw;
4613                return true;
4614            }
4615            this._dot_throw = return_false;
4616            return false;
4617        });
4618        def(AST_UnaryPrefix, function() {
4619            return this.operator == "void";
4620        });
4621        def(AST_UnaryPostfix, return_false);
4622        def(AST_Undefined, return_true);
4623    })(function(node, func) {
4624        node.DEFMETHOD("_dot_throw", func);
4625    });
4626
4627    (function(def) {
4628        def(AST_Node, return_false);
4629        def(AST_Array, return_true);
4630        function is_binary_defined(compressor, op, node) {
4631            switch (op) {
4632              case "&&":
4633                return node.left.is_defined(compressor) && node.right.is_defined(compressor);
4634              case "||":
4635                return node.left.is_truthy() || node.right.is_defined(compressor);
4636              case "??":
4637                return node.left.is_defined(compressor) || node.right.is_defined(compressor);
4638              default:
4639                return true;
4640            }
4641        }
4642        def(AST_Assign, function(compressor) {
4643            var op = this.operator;
4644            if (op == "=") return this.right.is_defined(compressor);
4645            return is_binary_defined(compressor, op.slice(0, -1), this);
4646        });
4647        def(AST_Binary, function(compressor) {
4648            return is_binary_defined(compressor, this.operator, this);
4649        });
4650        def(AST_Conditional, function(compressor) {
4651            return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
4652        });
4653        def(AST_Constant, return_true);
4654        def(AST_Hole, return_false);
4655        def(AST_Lambda, return_true);
4656        def(AST_Object, return_true);
4657        def(AST_Sequence, function(compressor) {
4658            return this.tail_node().is_defined(compressor);
4659        });
4660        def(AST_SymbolRef, function(compressor) {
4661            if (this.is_undefined) return false;
4662            if (is_undeclared_ref(this) && this.is_declared(compressor)) return true;
4663            if (this.is_immutable()) return true;
4664            var fixed = this.fixed_value();
4665            if (!fixed) return false;
4666            this.is_defined = return_false;
4667            var result = fixed.is_defined(compressor);
4668            delete this.is_defined;
4669            return result;
4670        });
4671        def(AST_UnaryPrefix, function() {
4672            return this.operator != "void";
4673        });
4674        def(AST_UnaryPostfix, return_true);
4675        def(AST_Undefined, return_false);
4676    })(function(node, func) {
4677        node.DEFMETHOD("is_defined", func);
4678    });
4679
4680    /* -----[ boolean/negation helpers ]----- */
4681
4682    // methods to determine whether an expression has a boolean result type
4683    (function(def) {
4684        def(AST_Node, return_false);
4685        def(AST_Assign, function(compressor) {
4686            return this.operator == "=" && this.right.is_boolean(compressor);
4687        });
4688        var binary = makePredicate("in instanceof == != === !== < <= >= >");
4689        def(AST_Binary, function(compressor) {
4690            return binary[this.operator] || lazy_op[this.operator]
4691                && this.left.is_boolean(compressor)
4692                && this.right.is_boolean(compressor);
4693        });
4694        def(AST_Boolean, return_true);
4695        var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some");
4696        def(AST_Call, function(compressor) {
4697            if (!compressor.option("unsafe")) return false;
4698            var exp = this.expression;
4699            return exp instanceof AST_Dot && (fn[exp.property]
4700                || exp.property == "test" && exp.expression instanceof AST_RegExp);
4701        });
4702        def(AST_Conditional, function(compressor) {
4703            return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
4704        });
4705        def(AST_New, return_false);
4706        def(AST_Sequence, function(compressor) {
4707            return this.tail_node().is_boolean(compressor);
4708        });
4709        def(AST_SymbolRef, function(compressor) {
4710            var fixed = this.fixed_value();
4711            if (!fixed) return false;
4712            this.is_boolean = return_false;
4713            var result = fixed.is_boolean(compressor);
4714            delete this.is_boolean;
4715            return result;
4716        });
4717        var unary = makePredicate("! delete");
4718        def(AST_UnaryPrefix, function() {
4719            return unary[this.operator];
4720        });
4721    })(function(node, func) {
4722        node.DEFMETHOD("is_boolean", func);
4723    });
4724
4725    // methods to determine if an expression has a numeric result type
4726    (function(def) {
4727        def(AST_Node, return_false);
4728        var binary = makePredicate("- * / % ** & | ^ << >> >>>");
4729        def(AST_Assign, function(compressor) {
4730            return binary[this.operator.slice(0, -1)]
4731                || this.operator == "=" && this.right.is_number(compressor);
4732        });
4733        def(AST_Binary, function(compressor) {
4734            if (binary[this.operator]) return true;
4735            if (this.operator != "+") return false;
4736            return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
4737                && (this.right.is_boolean(compressor) || this.right.is_number(compressor));
4738        });
4739        var fn = makePredicate([
4740            "charCodeAt",
4741            "getDate",
4742            "getDay",
4743            "getFullYear",
4744            "getHours",
4745            "getMilliseconds",
4746            "getMinutes",
4747            "getMonth",
4748            "getSeconds",
4749            "getTime",
4750            "getTimezoneOffset",
4751            "getUTCDate",
4752            "getUTCDay",
4753            "getUTCFullYear",
4754            "getUTCHours",
4755            "getUTCMilliseconds",
4756            "getUTCMinutes",
4757            "getUTCMonth",
4758            "getUTCSeconds",
4759            "getYear",
4760            "indexOf",
4761            "lastIndexOf",
4762            "localeCompare",
4763            "push",
4764            "search",
4765            "setDate",
4766            "setFullYear",
4767            "setHours",
4768            "setMilliseconds",
4769            "setMinutes",
4770            "setMonth",
4771            "setSeconds",
4772            "setTime",
4773            "setUTCDate",
4774            "setUTCFullYear",
4775            "setUTCHours",
4776            "setUTCMilliseconds",
4777            "setUTCMinutes",
4778            "setUTCMonth",
4779            "setUTCSeconds",
4780            "setYear",
4781        ]);
4782        def(AST_Call, function(compressor) {
4783            if (!compressor.option("unsafe")) return false;
4784            var exp = this.expression;
4785            return exp instanceof AST_Dot && (fn[exp.property]
4786                || is_undeclared_ref(exp.expression) && exp.expression.name == "Math");
4787        });
4788        def(AST_Conditional, function(compressor) {
4789            return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
4790        });
4791        def(AST_New, return_false);
4792        def(AST_Number, return_true);
4793        def(AST_Sequence, function(compressor) {
4794            return this.tail_node().is_number(compressor);
4795        });
4796        def(AST_SymbolRef, function(compressor, keep_unary) {
4797            var fixed = this.fixed_value();
4798            if (!fixed) return false;
4799            if (keep_unary
4800                && fixed instanceof AST_UnaryPrefix
4801                && fixed.operator == "+"
4802                && fixed.expression.equals(this)) {
4803                return false;
4804            }
4805            this.is_number = return_false;
4806            var result = fixed.is_number(compressor);
4807            delete this.is_number;
4808            return result;
4809        });
4810        var unary = makePredicate("+ - ~ ++ --");
4811        def(AST_Unary, function() {
4812            return unary[this.operator];
4813        });
4814    })(function(node, func) {
4815        node.DEFMETHOD("is_number", func);
4816    });
4817
4818    // methods to determine if an expression has a string result type
4819    (function(def) {
4820        def(AST_Node, return_false);
4821        def(AST_Assign, function(compressor) {
4822            switch (this.operator) {
4823              case "+=":
4824                if (this.left.is_string(compressor)) return true;
4825              case "=":
4826                return this.right.is_string(compressor);
4827            }
4828        });
4829        def(AST_Binary, function(compressor) {
4830            return this.operator == "+" &&
4831                (this.left.is_string(compressor) || this.right.is_string(compressor));
4832        });
4833        var fn = makePredicate([
4834            "charAt",
4835            "substr",
4836            "substring",
4837            "toExponential",
4838            "toFixed",
4839            "toLowerCase",
4840            "toPrecision",
4841            "toString",
4842            "toUpperCase",
4843            "trim",
4844        ]);
4845        def(AST_Call, function(compressor) {
4846            if (!compressor.option("unsafe")) return false;
4847            var exp = this.expression;
4848            return exp instanceof AST_Dot && fn[exp.property];
4849        });
4850        def(AST_Conditional, function(compressor) {
4851            return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
4852        });
4853        def(AST_Sequence, function(compressor) {
4854            return this.tail_node().is_string(compressor);
4855        });
4856        def(AST_String, return_true);
4857        def(AST_SymbolRef, function(compressor) {
4858            var fixed = this.fixed_value();
4859            if (!fixed) return false;
4860            this.is_string = return_false;
4861            var result = fixed.is_string(compressor);
4862            delete this.is_string;
4863            return result;
4864        });
4865        def(AST_Template, function(compressor) {
4866            return !this.tag || is_raw_tag(compressor, this.tag);
4867        });
4868        def(AST_UnaryPrefix, function() {
4869            return this.operator == "typeof";
4870        });
4871    })(function(node, func) {
4872        node.DEFMETHOD("is_string", func);
4873    });
4874
4875    var lazy_op = makePredicate("&& || ??");
4876
4877    (function(def) {
4878        function to_node(value, orig) {
4879            if (value instanceof AST_Node) return value.clone(true);
4880            if (Array.isArray(value)) return make_node(AST_Array, orig, {
4881                elements: value.map(function(value) {
4882                    return to_node(value, orig);
4883                })
4884            });
4885            if (value && typeof value == "object") {
4886                var props = [];
4887                for (var key in value) if (HOP(value, key)) {
4888                    props.push(make_node(AST_ObjectKeyVal, orig, {
4889                        key: key,
4890                        value: to_node(value[key], orig),
4891                    }));
4892                }
4893                return make_node(AST_Object, orig, { properties: props });
4894            }
4895            return make_node_from_constant(value, orig);
4896        }
4897
4898        function warn(node) {
4899            AST_Node.warn("global_defs {this} redefined [{start}]", node);
4900        }
4901
4902        AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
4903            if (!compressor.option("global_defs")) return this;
4904            this.figure_out_scope({ ie: compressor.option("ie") });
4905            return this.transform(new TreeTransformer(function(node) {
4906                var def = node._find_defs(compressor, "");
4907                if (!def) return;
4908                var level = 0, child = node, parent;
4909                while (parent = this.parent(level++)) {
4910                    if (!(parent instanceof AST_PropAccess)) break;
4911                    if (parent.expression !== child) break;
4912                    child = parent;
4913                }
4914                if (is_lhs(child, parent)) {
4915                    warn(node);
4916                    return;
4917                }
4918                return def;
4919            }));
4920        });
4921        def(AST_Node, noop);
4922        def(AST_Dot, function(compressor, suffix) {
4923            return this.expression._find_defs(compressor, "." + this.property + suffix);
4924        });
4925        def(AST_SymbolDeclaration, function(compressor) {
4926            if (!this.definition().global) return;
4927            if (HOP(compressor.option("global_defs"), this.name)) warn(this);
4928        });
4929        def(AST_SymbolRef, function(compressor, suffix) {
4930            if (!this.definition().global) return;
4931            var defines = compressor.option("global_defs");
4932            var name = this.name + suffix;
4933            if (HOP(defines, name)) return to_node(defines[name], this);
4934        });
4935    })(function(node, func) {
4936        node.DEFMETHOD("_find_defs", func);
4937    });
4938
4939    function best_of_expression(ast1, ast2, threshold) {
4940        var delta = ast2.print_to_string().length - ast1.print_to_string().length;
4941        return delta < (threshold || 0) ? ast2 : ast1;
4942    }
4943
4944    function best_of_statement(ast1, ast2, threshold) {
4945        return best_of_expression(make_node(AST_SimpleStatement, ast1, {
4946            body: ast1,
4947        }), make_node(AST_SimpleStatement, ast2, {
4948            body: ast2,
4949        }), threshold).body;
4950    }
4951
4952    function best_of(compressor, ast1, ast2, threshold) {
4953        return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
4954    }
4955
4956    function convert_to_predicate(obj) {
4957        var map = Object.create(null);
4958        Object.keys(obj).forEach(function(key) {
4959            map[key] = makePredicate(obj[key]);
4960        });
4961        return map;
4962    }
4963
4964    function skip_directives(body) {
4965        for (var i = 0; i < body.length; i++) {
4966            var stat = body[i];
4967            if (!(stat instanceof AST_Directive)) return stat;
4968        }
4969    }
4970
4971    function arrow_first_statement() {
4972        if (this.value) return make_node(AST_Return, this.value, { value: this.value });
4973        return skip_directives(this.body);
4974    }
4975    AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement);
4976    AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement);
4977    AST_Lambda.DEFMETHOD("first_statement", function() {
4978        return skip_directives(this.body);
4979    });
4980
4981    AST_Lambda.DEFMETHOD("length", function() {
4982        var argnames = this.argnames;
4983        for (var i = 0; i < argnames.length; i++) {
4984            if (argnames[i] instanceof AST_DefaultValue) break;
4985        }
4986        return i;
4987    });
4988
4989    function try_evaluate(compressor, node) {
4990        var ev = node.evaluate(compressor);
4991        if (ev === node) return node;
4992        ev = make_node_from_constant(ev, node).optimize(compressor);
4993        return best_of(compressor, node, ev, compressor.eval_threshold);
4994    }
4995
4996    var object_fns = [
4997        "constructor",
4998        "toString",
4999        "valueOf",
5000    ];
5001    var native_fns = convert_to_predicate({
5002        Array: [
5003            "indexOf",
5004            "join",
5005            "lastIndexOf",
5006            "slice",
5007        ].concat(object_fns),
5008        Boolean: object_fns,
5009        Function: object_fns,
5010        Number: [
5011            "toExponential",
5012            "toFixed",
5013            "toPrecision",
5014        ].concat(object_fns),
5015        Object: object_fns,
5016        RegExp: [
5017            "exec",
5018            "test",
5019        ].concat(object_fns),
5020        String: [
5021            "charAt",
5022            "charCodeAt",
5023            "concat",
5024            "indexOf",
5025            "italics",
5026            "lastIndexOf",
5027            "match",
5028            "replace",
5029            "search",
5030            "slice",
5031            "split",
5032            "substr",
5033            "substring",
5034            "toLowerCase",
5035            "toUpperCase",
5036            "trim",
5037        ].concat(object_fns),
5038    });
5039    var static_fns = convert_to_predicate({
5040        Array: [
5041            "isArray",
5042        ],
5043        Math: [
5044            "abs",
5045            "acos",
5046            "asin",
5047            "atan",
5048            "ceil",
5049            "cos",
5050            "exp",
5051            "floor",
5052            "log",
5053            "round",
5054            "sin",
5055            "sqrt",
5056            "tan",
5057            "atan2",
5058            "pow",
5059            "max",
5060            "min",
5061        ],
5062        Number: [
5063            "isFinite",
5064            "isNaN",
5065        ],
5066        Object: [
5067            "create",
5068            "getOwnPropertyDescriptor",
5069            "getOwnPropertyNames",
5070            "getPrototypeOf",
5071            "isExtensible",
5072            "isFrozen",
5073            "isSealed",
5074            "keys",
5075        ],
5076        String: [
5077            "fromCharCode",
5078            "raw",
5079        ],
5080    });
5081
5082    function is_static_fn(node) {
5083        if (!(node instanceof AST_Dot)) return false;
5084        var expr = node.expression;
5085        if (!is_undeclared_ref(expr)) return false;
5086        var static_fn = static_fns[expr.name];
5087        return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random");
5088    }
5089
5090    // Accommodate when compress option evaluate=false
5091    // as well as the common constant expressions !0 and -1
5092    (function(def) {
5093        def(AST_Node, return_false);
5094        def(AST_Constant, return_true);
5095        def(AST_RegExp, return_false);
5096        var unaryPrefix = makePredicate("! ~ - + void");
5097        def(AST_UnaryPrefix, function() {
5098            return unaryPrefix[this.operator] && this.expression instanceof AST_Constant;
5099        });
5100    })(function(node, func) {
5101        node.DEFMETHOD("is_constant", func);
5102    });
5103
5104    // methods to evaluate a constant expression
5105    (function(def) {
5106        // If the node has been successfully reduced to a constant,
5107        // then its value is returned; otherwise the element itself
5108        // is returned.
5109        //
5110        // They can be distinguished as constant value is never a
5111        // descendant of AST_Node.
5112        //
5113        // When `ignore_side_effects` is `true`, inspect the constant value
5114        // produced without worrying about any side effects caused by said
5115        // expression.
5116        AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
5117            if (!compressor.option("evaluate")) return this;
5118            var cached = [];
5119            var val = this._eval(compressor, ignore_side_effects, cached, 1);
5120            cached.forEach(function(node) {
5121                delete node._eval;
5122            });
5123            if (ignore_side_effects) return val;
5124            if (!val || val instanceof RegExp) return val;
5125            if (typeof val == "function" || typeof val == "object") return this;
5126            return val;
5127        });
5128        var scan_modified = new TreeWalker(function(node) {
5129            if (node instanceof AST_Assign) modified(node.left);
5130            if (node instanceof AST_ForEnumeration) modified(node.init);
5131            if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression);
5132        });
5133        function modified(node) {
5134            if (node instanceof AST_DestructuredArray) {
5135                node.elements.forEach(modified);
5136            } else if (node instanceof AST_DestructuredObject) {
5137                node.properties.forEach(function(prop) {
5138                    modified(prop.value);
5139                });
5140            } else if (node instanceof AST_PropAccess) {
5141                modified(node.expression);
5142            } else if (node instanceof AST_SymbolRef) {
5143                node.definition().references.forEach(function(ref) {
5144                    delete ref._eval;
5145                });
5146            }
5147        }
5148        def(AST_Statement, function() {
5149            throw new Error(string_template("Cannot evaluate a statement [{start}]", this));
5150        });
5151        def(AST_Accessor, return_this);
5152        def(AST_BigInt, return_this);
5153        def(AST_Class, return_this);
5154        def(AST_Node, return_this);
5155        def(AST_Constant, function() {
5156            return this.value;
5157        });
5158        def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
5159            var lhs = this.left;
5160            if (!ignore_side_effects) {
5161                if (!(lhs instanceof AST_SymbolRef)) return this;
5162                if (!HOP(lhs, "_eval")) {
5163                    if (!lhs.fixed) return this;
5164                    var def = lhs.definition();
5165                    if (!def.fixed) return this;
5166                    if (def.undeclared) return this;
5167                    if (def.last_ref !== lhs) return this;
5168                    if (def.single_use == "m") return this;
5169                    if (this.right.has_side_effects(compressor)) return this;
5170                }
5171            }
5172            var op = this.operator;
5173            var node;
5174            if (!HOP(lhs, "_eval") && lhs instanceof AST_SymbolRef && lhs.fixed && lhs.definition().fixed) {
5175                node = lhs;
5176            } else if (op == "=") {
5177                node = this.right;
5178            } else {
5179                node = make_node(AST_Binary, this, {
5180                    operator: op.slice(0, -1),
5181                    left: lhs,
5182                    right: this.right,
5183                });
5184            }
5185            lhs.walk(scan_modified);
5186            var value = node._eval(compressor, ignore_side_effects, cached, depth);
5187            if (typeof value == "object") return this;
5188            modified(lhs);
5189            return value;
5190        });
5191        def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
5192            if (!ignore_side_effects) return this;
5193            var exprs = this.expressions;
5194            for (var i = 0, last = exprs.length - 1; i < last; i++) {
5195                exprs[i].walk(scan_modified);
5196            }
5197            var tail = exprs[last];
5198            var value = tail._eval(compressor, ignore_side_effects, cached, depth);
5199            return value === tail ? this : value;
5200        });
5201        def(AST_Lambda, function(compressor) {
5202            if (compressor.option("unsafe")) {
5203                var fn = function() {};
5204                fn.node = this;
5205                fn.toString = function() {
5206                    return "function(){}";
5207                };
5208                return fn;
5209            }
5210            return this;
5211        });
5212        def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
5213            if (compressor.option("unsafe")) {
5214                var elements = [];
5215                for (var i = 0; i < this.elements.length; i++) {
5216                    var element = this.elements[i];
5217                    if (element instanceof AST_Hole) return this;
5218                    var value = element._eval(compressor, ignore_side_effects, cached, depth);
5219                    if (element === value) return this;
5220                    elements.push(value);
5221                }
5222                return elements;
5223            }
5224            return this;
5225        });
5226        def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
5227            if (compressor.option("unsafe")) {
5228                var val = {};
5229                for (var i = 0; i < this.properties.length; i++) {
5230                    var prop = this.properties[i];
5231                    if (!(prop instanceof AST_ObjectKeyVal)) return this;
5232                    var key = prop.key;
5233                    if (key instanceof AST_Node) {
5234                        key = key._eval(compressor, ignore_side_effects, cached, depth);
5235                        if (key === prop.key) return this;
5236                    }
5237                    switch (key) {
5238                      case "__proto__":
5239                      case "toString":
5240                      case "valueOf":
5241                        return this;
5242                    }
5243                    val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
5244                    if (val[key] === prop.value) return this;
5245                }
5246                return val;
5247            }
5248            return this;
5249        });
5250        var non_converting_unary = makePredicate("! typeof void");
5251        def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
5252            var e = this.expression;
5253            var op = this.operator;
5254            // Function would be evaluated to an array and so typeof would
5255            // incorrectly return "object". Hence making is a special case.
5256            if (compressor.option("typeofs")
5257                && op == "typeof"
5258                && (e instanceof AST_Lambda
5259                    || e instanceof AST_SymbolRef
5260                        && e.fixed_value() instanceof AST_Lambda)) {
5261                return typeof function(){};
5262            }
5263            var def = e instanceof AST_SymbolRef && e.definition();
5264            if (!non_converting_unary[op] && !(def && def.fixed)) depth++;
5265            e.walk(scan_modified);
5266            var v = e._eval(compressor, ignore_side_effects, cached, depth);
5267            if (v === e) {
5268                if (ignore_side_effects && op == "void") return;
5269                return this;
5270            }
5271            switch (op) {
5272              case "!": return !v;
5273              case "typeof":
5274                // typeof <RegExp> returns "object" or "function" on different platforms
5275                // so cannot evaluate reliably
5276                if (v instanceof RegExp) return this;
5277                return typeof v;
5278              case "void": return;
5279              case "~": return ~v;
5280              case "-": return -v;
5281              case "+": return +v;
5282              case "++":
5283              case "--":
5284                if (!def) return this;
5285                if (!ignore_side_effects) {
5286                    if (def.undeclared) return this;
5287                    if (def.last_ref !== e) return this;
5288                }
5289                if (HOP(e, "_eval")) v = +(op[0] + 1) + +v;
5290                modified(e);
5291                return v;
5292            }
5293            return this;
5294        });
5295        def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) {
5296            var e = this.expression;
5297            if (!(e instanceof AST_SymbolRef)) {
5298                if (!ignore_side_effects) return this;
5299            } else if (!HOP(e, "_eval")) {
5300                if (!e.fixed) return this;
5301                if (!ignore_side_effects) {
5302                    var def = e.definition();
5303                    if (!def.fixed) return this;
5304                    if (def.undeclared) return this;
5305                    if (def.last_ref !== e) return this;
5306                }
5307            }
5308            if (!(e instanceof AST_SymbolRef && e.definition().fixed)) depth++;
5309            e.walk(scan_modified);
5310            var v = e._eval(compressor, ignore_side_effects, cached, depth);
5311            if (v === e) return this;
5312            modified(e);
5313            return +v;
5314        });
5315        var non_converting_binary = makePredicate("&& || === !==");
5316        def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
5317            if (!non_converting_binary[this.operator]) depth++;
5318            var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
5319            if (left === this.left) return this;
5320            if (this.operator == (left ? "||" : "&&")) return left;
5321            var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object");
5322            var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth);
5323            if (right === this.right) return this;
5324            var result;
5325            switch (this.operator) {
5326              case "&&" : result = left &&  right; break;
5327              case "||" : result = left ||  right; break;
5328              case "??" :
5329                result = left == null ? right : left;
5330                break;
5331              case "|"  : result = left |   right; break;
5332              case "&"  : result = left &   right; break;
5333              case "^"  : result = left ^   right; break;
5334              case "+"  : result = left +   right; break;
5335              case "-"  : result = left -   right; break;
5336              case "*"  : result = left *   right; break;
5337              case "/"  : result = left /   right; break;
5338              case "%"  : result = left %   right; break;
5339              case "<<" : result = left <<  right; break;
5340              case ">>" : result = left >>  right; break;
5341              case ">>>": result = left >>> right; break;
5342              case "==" : result = left ==  right; break;
5343              case "===": result = left === right; break;
5344              case "!=" : result = left !=  right; break;
5345              case "!==": result = left !== right; break;
5346              case "<"  : result = left <   right; break;
5347              case "<=" : result = left <=  right; break;
5348              case ">"  : result = left >   right; break;
5349              case ">=" : result = left >=  right; break;
5350              case "**":
5351                result = Math.pow(left, right);
5352                break;
5353              case "in":
5354                if (right && typeof right == "object" && HOP(right, left)) {
5355                    result = true;
5356                    break;
5357                }
5358              default:
5359                return this;
5360            }
5361            if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
5362            if (compressor.option("unsafe_math")
5363                && !ignore_side_effects
5364                && result
5365                && typeof result == "number"
5366                && (this.operator == "+" || this.operator == "-")) {
5367                var digits = Math.max(0, decimals(left), decimals(right));
5368                // 53-bit significand ---> 15.95 decimal places
5369                if (digits < 16) return +result.toFixed(digits);
5370            }
5371            return result;
5372
5373            function decimals(operand) {
5374                var match = /(\.[0-9]*)?(e[^e]+)?$/.exec(+operand);
5375                return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
5376            }
5377        });
5378        def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
5379            var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
5380            if (condition === this.condition) return this;
5381            var node = condition ? this.consequent : this.alternative;
5382            var value = node._eval(compressor, ignore_side_effects, cached, depth);
5383            return value === node ? this : value;
5384        });
5385        function verify_escaped(ref, depth) {
5386            var escaped = ref.definition().escaped;
5387            switch (escaped.length) {
5388              case 0:
5389                return true;
5390              case 1:
5391                var found = false;
5392                escaped[0].walk(new TreeWalker(function(node) {
5393                    if (found) return true;
5394                    if (node === ref) return found = true;
5395                    if (node instanceof AST_Scope) return true;
5396                }));
5397                return found;
5398              default:
5399                return depth <= escaped.depth;
5400            }
5401        }
5402        def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
5403            var fixed = this.fixed_value();
5404            if (!fixed) return this;
5405            var value;
5406            if (HOP(fixed, "_eval")) {
5407                value = fixed._eval();
5408            } else {
5409                this._eval = return_this;
5410                value = fixed._eval(compressor, ignore_side_effects, cached, depth);
5411                delete this._eval;
5412                if (value === fixed) return this;
5413                fixed._eval = function() {
5414                    return value;
5415                };
5416                cached.push(fixed);
5417            }
5418            return value && typeof value == "object" && !verify_escaped(this, depth) ? this : value;
5419        });
5420        var global_objs = {
5421            Array: Array,
5422            Math: Math,
5423            Number: Number,
5424            Object: Object,
5425            String: String,
5426        };
5427        var static_values = convert_to_predicate({
5428            Math: [
5429                "E",
5430                "LN10",
5431                "LN2",
5432                "LOG2E",
5433                "LOG10E",
5434                "PI",
5435                "SQRT1_2",
5436                "SQRT2",
5437            ],
5438            Number: [
5439                "MAX_VALUE",
5440                "MIN_VALUE",
5441                "NaN",
5442                "NEGATIVE_INFINITY",
5443                "POSITIVE_INFINITY",
5444            ],
5445        });
5446        var regexp_props = makePredicate("global ignoreCase multiline source");
5447        def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
5448            if (compressor.option("unsafe")) {
5449                var val;
5450                var exp = this.expression;
5451                if (!is_undeclared_ref(exp)) {
5452                    val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
5453                    if (val == null || val === exp) return this;
5454                }
5455                var key = this.property;
5456                if (key instanceof AST_Node) {
5457                    key = key._eval(compressor, ignore_side_effects, cached, depth);
5458                    if (key === this.property) return this;
5459                }
5460                if (val === undefined) {
5461                    var static_value = static_values[exp.name];
5462                    if (!static_value || !static_value[key]) return this;
5463                    val = global_objs[exp.name];
5464                } else if (val instanceof RegExp) {
5465                    if (!regexp_props[key]) return this;
5466                } else if (typeof val == "object") {
5467                    if (!HOP(val, key)) return this;
5468                } else if (typeof val == "function") switch (key) {
5469                  case "name":
5470                    return val.node.name ? val.node.name.name : "";
5471                  case "length":
5472                    return val.node.length();
5473                  default:
5474                    return this;
5475                }
5476                return val[key];
5477            }
5478            return this;
5479        });
5480        function eval_all(nodes, compressor, ignore_side_effects, cached, depth) {
5481            var values = [];
5482            for (var i = 0; i < nodes.length; i++) {
5483                var node = nodes[i];
5484                var value = node._eval(compressor, ignore_side_effects, cached, depth);
5485                if (node === value) return;
5486                values.push(value);
5487            }
5488            return values;
5489        }
5490        def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
5491            var exp = this.expression;
5492            var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
5493            if (fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function) {
5494                if (fn.evaluating) return this;
5495                if (fn.name && fn.name.definition().recursive_refs > 0) return this;
5496                if (this.is_expr_pure(compressor)) return this;
5497                var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
5498                if (!all(fn.argnames, function(sym, index) {
5499                    if (sym instanceof AST_DefaultValue) {
5500                        if (!args) return false;
5501                        if (args[index] === undefined) {
5502                            var value = sym.value._eval(compressor, ignore_side_effects, cached, depth);
5503                            if (value === sym.value) return false;
5504                            args[index] = value;
5505                        }
5506                        sym = sym.name;
5507                    }
5508                    return !(sym instanceof AST_Destructured);
5509                })) return this;
5510                if (fn.rest instanceof AST_Destructured) return this;
5511                if (!args && !ignore_side_effects) return this;
5512                var stat = fn.first_statement();
5513                if (!(stat instanceof AST_Return)) {
5514                    if (ignore_side_effects) {
5515                        fn.walk(scan_modified);
5516                        var found = false;
5517                        fn.evaluating = true;
5518                        walk_body(fn, new TreeWalker(function(node) {
5519                            if (found) return true;
5520                            if (node instanceof AST_Return) {
5521                                if (node.value && node.value._eval(compressor, true, cached, depth) !== undefined) {
5522                                    found = true;
5523                                }
5524                                return true;
5525                            }
5526                            if (node instanceof AST_Scope && node !== fn) return true;
5527                        }));
5528                        fn.evaluating = false;
5529                        if (!found) return;
5530                    }
5531                    return this;
5532                }
5533                var val = stat.value;
5534                if (!val) return;
5535                var cached_args = [];
5536                if (!args || all(fn.argnames, function(sym, i) {
5537                    return assign(sym, args[i]);
5538                }) && !(fn.rest && !assign(fn.rest, args.slice(fn.argnames.length))) || ignore_side_effects) {
5539                    if (ignore_side_effects) fn.argnames.forEach(function(sym) {
5540                        if (sym instanceof AST_DefaultValue) sym.value.walk(scan_modified);
5541                    });
5542                    fn.evaluating = true;
5543                    val = val._eval(compressor, ignore_side_effects, cached, depth);
5544                    fn.evaluating = false;
5545                }
5546                cached_args.forEach(function(node) {
5547                    delete node._eval;
5548                });
5549                return val === stat.value ? this : val;
5550            } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
5551                var key = exp.property;
5552                if (key instanceof AST_Node) {
5553                    key = key._eval(compressor, ignore_side_effects, cached, depth);
5554                    if (key === exp.property) return this;
5555                }
5556                var val;
5557                var e = exp.expression;
5558                if (is_undeclared_ref(e)) {
5559                    var static_fn = static_fns[e.name];
5560                    if (!static_fn || !static_fn[key]) return this;
5561                    val = global_objs[e.name];
5562                } else {
5563                    val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
5564                    if (val == null || val === e) return this;
5565                    var native_fn = native_fns[val.constructor.name];
5566                    if (!native_fn || !native_fn[key]) return this;
5567                    if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
5568                }
5569                var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
5570                if (!args) return this;
5571                if (key == "replace" && typeof args[1] == "function") return this;
5572                try {
5573                    return val[key].apply(val, args);
5574                } catch (ex) {
5575                    AST_Node.warn("Error evaluating {this} [{start}]", this);
5576                } finally {
5577                    if (val instanceof RegExp) val.lastIndex = 0;
5578                }
5579            }
5580            return this;
5581
5582            function assign(sym, arg) {
5583                if (sym instanceof AST_DefaultValue) sym = sym.name;
5584                var def = sym.definition();
5585                if (def.orig[def.orig.length - 1] !== sym) return false;
5586                var value = arg;
5587                def.references.forEach(function(node) {
5588                    node._eval = function() {
5589                        return value;
5590                    };
5591                    cached_args.push(node);
5592                });
5593                return true;
5594            }
5595        });
5596        def(AST_New, return_this);
5597        def(AST_Template, function(compressor, ignore_side_effects, cached, depth) {
5598            if (!compressor.option("templates")) return this;
5599            if (this.tag) {
5600                if (!is_raw_tag(compressor, this.tag)) return this;
5601                decode = function(str) {
5602                    return str;
5603                };
5604            }
5605            var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth);
5606            if (!exprs) return this;
5607            var malformed = false;
5608            var ret = decode(this.strings[0]);
5609            for (var i = 0; i < exprs.length; i++) {
5610                ret += exprs[i] + decode(this.strings[i + 1]);
5611            }
5612            if (!malformed) return ret;
5613            this._eval = return_this;
5614            return this;
5615
5616            function decode(str) {
5617                str = decode_template(str);
5618                if (typeof str != "string") malformed = true;
5619                return str;
5620            }
5621        });
5622    })(function(node, func) {
5623        node.DEFMETHOD("_eval", func);
5624    });
5625
5626    // method to negate an expression
5627    (function(def) {
5628        function basic_negation(exp) {
5629            return make_node(AST_UnaryPrefix, exp, {
5630                operator: "!",
5631                expression: exp,
5632            });
5633        }
5634        function best(orig, alt, first_in_statement) {
5635            var negated = basic_negation(orig);
5636            if (first_in_statement) return best_of_expression(negated, make_node(AST_SimpleStatement, alt, {
5637                body: alt,
5638            })) === negated ? negated : alt;
5639            return best_of_expression(negated, alt);
5640        }
5641        def(AST_Node, function() {
5642            return basic_negation(this);
5643        });
5644        def(AST_Statement, function() {
5645            throw new Error("Cannot negate a statement");
5646        });
5647        def(AST_Binary, function(compressor, first_in_statement) {
5648            var self = this.clone(), op = this.operator;
5649            if (compressor.option("unsafe_comps")) {
5650                switch (op) {
5651                  case "<=" : self.operator = ">"  ; return self;
5652                  case "<"  : self.operator = ">=" ; return self;
5653                  case ">=" : self.operator = "<"  ; return self;
5654                  case ">"  : self.operator = "<=" ; return self;
5655                }
5656            }
5657            switch (op) {
5658              case "==" : self.operator = "!="; return self;
5659              case "!=" : self.operator = "=="; return self;
5660              case "===": self.operator = "!=="; return self;
5661              case "!==": self.operator = "==="; return self;
5662              case "&&":
5663                self.operator = "||";
5664                self.left = self.left.negate(compressor, first_in_statement);
5665                self.right = self.right.negate(compressor);
5666                return best(this, self, first_in_statement);
5667              case "||":
5668                self.operator = "&&";
5669                self.left = self.left.negate(compressor, first_in_statement);
5670                self.right = self.right.negate(compressor);
5671                return best(this, self, first_in_statement);
5672            }
5673            return basic_negation(this);
5674        });
5675        def(AST_ClassExpression, function() {
5676            return basic_negation(this);
5677        });
5678        def(AST_Conditional, function(compressor, first_in_statement) {
5679            var self = this.clone();
5680            self.consequent = self.consequent.negate(compressor);
5681            self.alternative = self.alternative.negate(compressor);
5682            return best(this, self, first_in_statement);
5683        });
5684        def(AST_LambdaExpression, function() {
5685            return basic_negation(this);
5686        });
5687        def(AST_Sequence, function(compressor) {
5688            var expressions = this.expressions.slice();
5689            expressions.push(expressions.pop().negate(compressor));
5690            return make_sequence(this, expressions);
5691        });
5692        def(AST_UnaryPrefix, function() {
5693            if (this.operator == "!")
5694                return this.expression;
5695            return basic_negation(this);
5696        });
5697    })(function(node, func) {
5698        node.DEFMETHOD("negate", function(compressor, first_in_statement) {
5699            return func.call(this, compressor, first_in_statement);
5700        });
5701    });
5702
5703    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");
5704    var global_pure_constructors = makePredicate("Map Set WeakMap WeakSet");
5705    AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
5706        if (compressor.option("unsafe")) {
5707            var expr = this.expression;
5708            if (is_undeclared_ref(expr)) {
5709                if (global_pure_fns[expr.name]) return true;
5710                if (this instanceof AST_New && global_pure_constructors[expr.name]) return true;
5711            }
5712            if (is_static_fn(expr)) return true;
5713        }
5714        return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this);
5715    });
5716    AST_Template.DEFMETHOD("is_expr_pure", function(compressor) {
5717        var tag = this.tag;
5718        if (!tag) return true;
5719        if (compressor.option("unsafe")) {
5720            if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true;
5721            if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) {
5722                var static_fn = static_fns[tag.expression.name];
5723                return static_fn && (static_fn[tag.property]
5724                    || tag.expression.name == "Math" && tag.property == "random");
5725            }
5726        }
5727        return !compressor.pure_funcs(this);
5728    });
5729    AST_Node.DEFMETHOD("is_call_pure", return_false);
5730    AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
5731        if (!compressor.option("unsafe")) return false;
5732        var dot = this.expression;
5733        if (!(dot instanceof AST_Dot)) return false;
5734        var exp = dot.expression;
5735        var map;
5736        var prop = dot.property;
5737        if (exp instanceof AST_Array) {
5738            map = native_fns.Array;
5739        } else if (exp.is_boolean(compressor)) {
5740            map = native_fns.Boolean;
5741        } else if (exp.is_number(compressor)) {
5742            map = native_fns.Number;
5743        } else if (exp instanceof AST_RegExp) {
5744            map = native_fns.RegExp;
5745        } else if (exp.is_string(compressor)) {
5746            map = native_fns.String;
5747            if (prop == "replace") {
5748                var arg = this.args[1];
5749                if (arg && !arg.is_string(compressor)) return false;
5750            }
5751        } else if (!dot.may_throw_on_access(compressor)) {
5752            map = native_fns.Object;
5753        }
5754        return map && map[prop];
5755    });
5756
5757    // determine if object spread syntax may cause runtime exception
5758    (function(def) {
5759        def(AST_Node, return_false);
5760        def(AST_Array, return_true);
5761        def(AST_Assign, function() {
5762            switch (this.operator) {
5763              case "=":
5764                return this.right.safe_to_spread();
5765              case "&&=":
5766              case "||=":
5767              case "??=":
5768                return this.left.safe_to_spread() && this.right.safe_to_spread();
5769            }
5770            return true;
5771        });
5772        def(AST_Binary, function() {
5773            return !lazy_op[this.operator] || this.left.safe_to_spread() && this.right.safe_to_spread();
5774        });
5775        def(AST_Constant, return_true);
5776        def(AST_Lambda, return_true);
5777        def(AST_Object, function() {
5778            return all(this.properties, function(prop) {
5779                return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
5780            });
5781        });
5782        def(AST_Sequence, function() {
5783            return this.tail_node().safe_to_spread();
5784        });
5785        def(AST_SymbolRef, function() {
5786            var fixed = this.fixed_value();
5787            return fixed && fixed.safe_to_spread();
5788        });
5789        def(AST_Unary, return_true);
5790    })(function(node, func) {
5791        node.DEFMETHOD("safe_to_spread", func);
5792    });
5793
5794    // determine if expression has side effects
5795    (function(def) {
5796        function any(list, compressor, spread) {
5797            return !all(list, spread ? function(node) {
5798                return node instanceof AST_Spread ? !spread(node, compressor) : !node.has_side_effects(compressor);
5799            } : function(node) {
5800                return !node.has_side_effects(compressor);
5801            });
5802        }
5803        function array_spread(node, compressor) {
5804            var exp = node.expression;
5805            return !exp.is_string(compressor) || exp.has_side_effects(compressor);
5806        }
5807        def(AST_Node, return_true);
5808        def(AST_Array, function(compressor) {
5809            return any(this.elements, compressor, array_spread);
5810        });
5811        def(AST_Assign, function(compressor) {
5812            var lhs = this.left;
5813            if (!(lhs instanceof AST_PropAccess)) return true;
5814            var node = lhs.expression;
5815            return !(node instanceof AST_ObjectIdentity)
5816                || !node.scope.resolve().new
5817                || lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
5818                || this.right.has_side_effects(compressor);
5819        });
5820        def(AST_Binary, function(compressor) {
5821            return this.left.has_side_effects(compressor)
5822                || this.right.has_side_effects(compressor)
5823                || !can_drop_op(this.operator, this.right, compressor);
5824        });
5825        def(AST_Block, function(compressor) {
5826            return any(this.body, compressor);
5827        });
5828        def(AST_Call, function(compressor) {
5829            if (!this.is_expr_pure(compressor)
5830                && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) {
5831                return true;
5832            }
5833            return any(this.args, compressor, array_spread);
5834        });
5835        def(AST_Case, function(compressor) {
5836            return this.expression.has_side_effects(compressor)
5837                || any(this.body, compressor);
5838        });
5839        def(AST_Class, function(compressor) {
5840            var base = this.extends;
5841            if (base) {
5842                if (base instanceof AST_SymbolRef) base = base.fixed_value();
5843                if (!safe_for_extends(base)) return true;
5844            }
5845            return any(this.properties, compressor);
5846        });
5847        def(AST_ClassProperty, function(compressor) {
5848            return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5849                || this.static && this.value && this.value.has_side_effects(compressor);
5850        });
5851        def(AST_Conditional, function(compressor) {
5852            return this.condition.has_side_effects(compressor)
5853                || this.consequent.has_side_effects(compressor)
5854                || this.alternative.has_side_effects(compressor);
5855        });
5856        def(AST_Constant, return_false);
5857        def(AST_Definitions, function(compressor) {
5858            return any(this.definitions, compressor);
5859        });
5860        def(AST_DestructuredArray, function(compressor) {
5861            return any(this.elements, compressor);
5862        });
5863        def(AST_DestructuredKeyVal, function(compressor) {
5864            return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5865                || this.value.has_side_effects(compressor);
5866        });
5867        def(AST_DestructuredObject, function(compressor) {
5868            return any(this.properties, compressor);
5869        });
5870        def(AST_Dot, function(compressor) {
5871            return this.expression.may_throw_on_access(compressor)
5872                || this.expression.has_side_effects(compressor);
5873        });
5874        def(AST_EmptyStatement, return_false);
5875        def(AST_If, function(compressor) {
5876            return this.condition.has_side_effects(compressor)
5877                || this.body && this.body.has_side_effects(compressor)
5878                || this.alternative && this.alternative.has_side_effects(compressor);
5879        });
5880        def(AST_LabeledStatement, function(compressor) {
5881            return this.body.has_side_effects(compressor);
5882        });
5883        def(AST_Lambda, return_false);
5884        def(AST_Object, function(compressor) {
5885            return any(this.properties, compressor, function(node, compressor) {
5886                var exp = node.expression;
5887                return !exp.safe_to_spread() || exp.has_side_effects(compressor);
5888            });
5889        });
5890        def(AST_ObjectIdentity, return_false);
5891        def(AST_ObjectProperty, function(compressor) {
5892            return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5893                || this.value.has_side_effects(compressor);
5894        });
5895        def(AST_Sequence, function(compressor) {
5896            return any(this.expressions, compressor);
5897        });
5898        def(AST_SimpleStatement, function(compressor) {
5899            return this.body.has_side_effects(compressor);
5900        });
5901        def(AST_Sub, function(compressor) {
5902            return this.expression.may_throw_on_access(compressor)
5903                || this.expression.has_side_effects(compressor)
5904                || this.property.has_side_effects(compressor);
5905        });
5906        def(AST_Switch, function(compressor) {
5907            return this.expression.has_side_effects(compressor)
5908                || any(this.body, compressor);
5909        });
5910        def(AST_SymbolDeclaration, return_false);
5911        def(AST_SymbolRef, function(compressor) {
5912            return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5913        });
5914        def(AST_Template, function(compressor) {
5915            return !this.is_expr_pure(compressor) || any(this.expressions, compressor);
5916        });
5917        def(AST_Try, function(compressor) {
5918            return any(this.body, compressor)
5919                || this.bcatch && this.bcatch.has_side_effects(compressor)
5920                || this.bfinally && this.bfinally.has_side_effects(compressor);
5921        });
5922        def(AST_Unary, function(compressor) {
5923            return unary_side_effects[this.operator]
5924                || this.expression.has_side_effects(compressor);
5925        });
5926        def(AST_VarDef, function() {
5927            return this.value;
5928        });
5929    })(function(node, func) {
5930        node.DEFMETHOD("has_side_effects", func);
5931    });
5932
5933    // determine if expression may throw
5934    (function(def) {
5935        def(AST_Node, return_true);
5936
5937        def(AST_Constant, return_false);
5938        def(AST_EmptyStatement, return_false);
5939        def(AST_Lambda, return_false);
5940        def(AST_ObjectIdentity, return_false);
5941        def(AST_SymbolDeclaration, return_false);
5942
5943        function any(list, compressor) {
5944            for (var i = list.length; --i >= 0;)
5945                if (list[i].may_throw(compressor))
5946                    return true;
5947            return false;
5948        }
5949
5950        function call_may_throw(exp, compressor) {
5951            if (exp.may_throw(compressor)) return true;
5952            if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
5953            if (!(exp instanceof AST_Lambda)) return true;
5954            if (any(exp.argnames, compressor)) return true;
5955            if (any(exp.body, compressor)) return true;
5956            return is_arrow(exp) && exp.value && exp.value.may_throw(compressor);
5957        }
5958
5959        def(AST_Array, function(compressor) {
5960            return any(this.elements, compressor);
5961        });
5962        def(AST_Assign, function(compressor) {
5963            if (this.right.may_throw(compressor)) return true;
5964            if (!compressor.has_directive("use strict")
5965                && this.operator == "="
5966                && this.left instanceof AST_SymbolRef) {
5967                return false;
5968            }
5969            return this.left.may_throw(compressor);
5970        });
5971        def(AST_Await, function(compressor) {
5972            return this.expression.may_throw(compressor);
5973        });
5974        def(AST_Binary, function(compressor) {
5975            return this.left.may_throw(compressor)
5976                || this.right.may_throw(compressor)
5977                || !can_drop_op(this.operator, this.right, compressor);
5978        });
5979        def(AST_Block, function(compressor) {
5980            return any(this.body, compressor);
5981        });
5982        def(AST_Call, function(compressor) {
5983            if (any(this.args, compressor)) return true;
5984            if (this.is_expr_pure(compressor)) return false;
5985            this.may_throw = return_true;
5986            var ret = call_may_throw(this.expression, compressor);
5987            delete this.may_throw;
5988            return ret;
5989        });
5990        def(AST_Case, function(compressor) {
5991            return this.expression.may_throw(compressor)
5992                || any(this.body, compressor);
5993        });
5994        def(AST_Conditional, function(compressor) {
5995            return this.condition.may_throw(compressor)
5996                || this.consequent.may_throw(compressor)
5997                || this.alternative.may_throw(compressor);
5998        });
5999        def(AST_DefaultValue, function(compressor) {
6000            return this.name.may_throw(compressor)
6001                || this.value && this.value.may_throw(compressor);
6002        });
6003        def(AST_Definitions, function(compressor) {
6004            return any(this.definitions, compressor);
6005        });
6006        def(AST_Dot, function(compressor) {
6007            return !this.optional && this.expression.may_throw_on_access(compressor)
6008                || this.expression.may_throw(compressor);
6009        });
6010        def(AST_ForEnumeration, function(compressor) {
6011            if (this.init.may_throw(compressor)) return true;
6012            var obj = this.object;
6013            if (obj.may_throw(compressor)) return true;
6014            obj = obj.tail_node();
6015            if (!(obj instanceof AST_Array || obj.is_string(compressor))) return true;
6016            return this.body.may_throw(compressor);
6017        });
6018        def(AST_If, function(compressor) {
6019            return this.condition.may_throw(compressor)
6020                || this.body && this.body.may_throw(compressor)
6021                || this.alternative && this.alternative.may_throw(compressor);
6022        });
6023        def(AST_LabeledStatement, function(compressor) {
6024            return this.body.may_throw(compressor);
6025        });
6026        def(AST_Object, function(compressor) {
6027            return any(this.properties, compressor);
6028        });
6029        def(AST_ObjectProperty, function(compressor) {
6030            return this.value.may_throw(compressor)
6031                || this.key instanceof AST_Node && this.key.may_throw(compressor);
6032        });
6033        def(AST_Return, function(compressor) {
6034            return this.value && this.value.may_throw(compressor);
6035        });
6036        def(AST_Sequence, function(compressor) {
6037            return any(this.expressions, compressor);
6038        });
6039        def(AST_SimpleStatement, function(compressor) {
6040            return this.body.may_throw(compressor);
6041        });
6042        def(AST_Sub, function(compressor) {
6043            return !this.optional && this.expression.may_throw_on_access(compressor)
6044                || this.expression.may_throw(compressor)
6045                || this.property.may_throw(compressor);
6046        });
6047        def(AST_Switch, function(compressor) {
6048            return this.expression.may_throw(compressor)
6049                || any(this.body, compressor);
6050        });
6051        def(AST_SymbolRef, function(compressor) {
6052            return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
6053        });
6054        def(AST_Template, function(compressor) {
6055            if (any(this.expressions, compressor)) return true;
6056            if (this.is_expr_pure(compressor)) return false;
6057            if (!this.tag) return false;
6058            this.may_throw = return_true;
6059            var ret = call_may_throw(this.tag, compressor);
6060            delete this.may_throw;
6061            return ret;
6062        });
6063        def(AST_Try, function(compressor) {
6064            return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor))
6065                || this.bfinally && this.bfinally.may_throw(compressor);
6066        });
6067        def(AST_Unary, function(compressor) {
6068            return this.expression.may_throw(compressor)
6069                && !(this.operator == "typeof" && this.expression instanceof AST_SymbolRef);
6070        });
6071        def(AST_VarDef, function(compressor) {
6072            return this.name.may_throw(compressor)
6073                || this.value && this.value.may_throw(compressor);
6074        });
6075    })(function(node, func) {
6076        node.DEFMETHOD("may_throw", func);
6077    });
6078
6079    // determine if expression is constant
6080    (function(def) {
6081        function all_constant(list, scope) {
6082            for (var i = list.length; --i >= 0;)
6083                if (!list[i].is_constant_expression(scope))
6084                    return false;
6085            return true;
6086        }
6087        def(AST_Node, return_false);
6088        def(AST_Array, function(scope) {
6089            return all_constant(this.elements, scope);
6090        });
6091        def(AST_Binary, function(scope) {
6092            return this.left.is_constant_expression(scope)
6093                && this.right.is_constant_expression(scope)
6094                && can_drop_op(this.operator, this.right);
6095        });
6096        def(AST_Class, function(scope) {
6097            var base = this.extends;
6098            if (base && !safe_for_extends(base)) return false;
6099            return all_constant(this.properties, scope);
6100        });
6101        def(AST_ClassProperty, function(scope) {
6102            return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope));
6103        });
6104        def(AST_Constant, return_true);
6105        def(AST_Lambda, function(scope) {
6106            var self = this;
6107            var result = true;
6108            var scopes = [];
6109            self.walk(new TreeWalker(function(node, descend) {
6110                if (!result) return true;
6111                if (node instanceof AST_BlockScope) {
6112                    if (node === self) return;
6113                    scopes.push(node);
6114                    descend();
6115                    scopes.pop();
6116                    return true;
6117                }
6118                if (node instanceof AST_SymbolRef) {
6119                    if (self.inlined || node.redef || node.in_arg) {
6120                        result = false;
6121                        return true;
6122                    }
6123                    if (self.variables.has(node.name)) return true;
6124                    var def = node.definition();
6125                    if (member(def.scope, scopes)) return true;
6126                    if (scope && !def.redefined()) {
6127                        var scope_def = scope.find_variable(node.name);
6128                        if (scope_def ? scope_def === def : def.undeclared) {
6129                            result = "f";
6130                            return true;
6131                        }
6132                    }
6133                    result = false;
6134                    return true;
6135                }
6136                if (node instanceof AST_ObjectIdentity) {
6137                    if (is_arrow(self) && all(scopes, function(s) {
6138                        return !(s instanceof AST_Scope) || is_arrow(s);
6139                    })) result = false;
6140                    return true;
6141                }
6142            }));
6143            return result;
6144        });
6145        def(AST_Object, function(scope) {
6146            return all_constant(this.properties, scope);
6147        });
6148        def(AST_ObjectProperty, function(scope) {
6149            return typeof this.key == "string" && this.value.is_constant_expression(scope);
6150        });
6151        def(AST_Unary, function(scope) {
6152            return this.expression.is_constant_expression(scope);
6153        });
6154    })(function(node, func) {
6155        node.DEFMETHOD("is_constant_expression", func);
6156    });
6157
6158    // tell me if a statement aborts
6159    function aborts(thing) {
6160        return thing && thing.aborts();
6161    }
6162    (function(def) {
6163        def(AST_Statement, return_null);
6164        def(AST_Jump, return_this);
6165        function block_aborts() {
6166            var n = this.body.length;
6167            return n > 0 && aborts(this.body[n - 1]);
6168        }
6169        def(AST_BlockStatement, block_aborts);
6170        def(AST_SwitchBranch, block_aborts);
6171        def(AST_If, function() {
6172            return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
6173        });
6174    })(function(node, func) {
6175        node.DEFMETHOD("aborts", func);
6176    });
6177
6178    /* -----[ optimizers ]----- */
6179
6180    var directives = makePredicate(["use asm", "use strict"]);
6181    OPT(AST_Directive, function(self, compressor) {
6182        if (compressor.option("directives")
6183            && (!directives[self.value] || compressor.has_directive(self.value) !== self)) {
6184            return make_node(AST_EmptyStatement, self);
6185        }
6186        return self;
6187    });
6188
6189    OPT(AST_Debugger, function(self, compressor) {
6190        if (compressor.option("drop_debugger"))
6191            return make_node(AST_EmptyStatement, self);
6192        return self;
6193    });
6194
6195    OPT(AST_LabeledStatement, function(self, compressor) {
6196        if (self.body instanceof AST_If || self.body instanceof AST_Break) {
6197            var body = tighten_body([ self.body ], compressor);
6198            switch (body.length) {
6199              case 0:
6200                self.body = make_node(AST_EmptyStatement, self);
6201                break;
6202              case 1:
6203                self.body = body[0];
6204                break;
6205              default:
6206                self.body = make_node(AST_BlockStatement, self, { body: body });
6207                break;
6208            }
6209        }
6210        return compressor.option("unused") && self.label.references.length == 0 ? self.body : self;
6211    });
6212
6213    OPT(AST_LoopControl, function(self, compressor) {
6214        if (!compressor.option("dead_code")) return self;
6215        var label = self.label;
6216        if (label) {
6217            var lct = compressor.loopcontrol_target(self);
6218            self.label = null;
6219            if (compressor.loopcontrol_target(self) === lct) {
6220                remove(label.thedef.references, self);
6221            } else {
6222                self.label = label;
6223            }
6224        }
6225        return self;
6226    });
6227
6228    OPT(AST_Block, function(self, compressor) {
6229        self.body = tighten_body(self.body, compressor);
6230        return self;
6231    });
6232
6233    function trim_block(node, parent, in_list) {
6234        switch (node.body.length) {
6235          case 0:
6236            return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6237          case 1:
6238            var stat = node.body[0];
6239            if (!safe_to_trim(stat)) return node;
6240            if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node;
6241            return stat;
6242        }
6243        return node;
6244    }
6245
6246    OPT(AST_BlockStatement, function(self, compressor) {
6247        self.body = tighten_body(self.body, compressor);
6248        return trim_block(self, compressor.parent());
6249    });
6250
6251    function drop_rest_farg(fn, compressor) {
6252        if (!compressor.option("rests")) return;
6253        if (fn.uses_arguments) return;
6254        if (!(fn.rest instanceof AST_DestructuredArray)) return;
6255        if (!compressor.drop_fargs(fn, compressor.parent())) return;
6256        fn.argnames = fn.argnames.concat(fn.rest.elements);
6257        fn.rest = fn.rest.rest;
6258    }
6259
6260    OPT(AST_Lambda, function(self, compressor) {
6261        drop_rest_farg(self, compressor);
6262        self.body = tighten_body(self.body, compressor);
6263        return self;
6264    });
6265
6266    function opt_arrow(self, compressor) {
6267        if (!compressor.option("arrows")) return self;
6268        drop_rest_farg(self, compressor);
6269        if (self.value) self.body = [ self.first_statement() ];
6270        var body = tighten_body(self.body, compressor);
6271        switch (body.length) {
6272          case 1:
6273            var stat = body[0];
6274            if (stat instanceof AST_Return) {
6275                self.body.length = 0;
6276                self.value = stat.value;
6277                break;
6278            }
6279          default:
6280            self.body = body;
6281            self.value = null;
6282            break;
6283        }
6284        return self;
6285    }
6286    OPT(AST_Arrow, opt_arrow);
6287    OPT(AST_AsyncArrow, opt_arrow);
6288
6289    OPT(AST_Function, function(self, compressor) {
6290        drop_rest_farg(self, compressor);
6291        self.body = tighten_body(self.body, compressor);
6292        var parent = compressor.parent();
6293        if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
6294            var stat = self.body[i];
6295            if (stat instanceof AST_Directive) continue;
6296            if (stat instanceof AST_Return) {
6297                if (i != self.body.length - 1) break;
6298                var call = stat.value;
6299                if (!call || call.TYPE != "Call") break;
6300                if (call.is_expr_pure(compressor)) break;
6301                var exp = call.expression, fn;
6302                if (!(exp instanceof AST_SymbolRef)) {
6303                    fn = exp;
6304                } else if (self.name && self.name.definition() === exp.definition()) {
6305                    break;
6306                } else {
6307                    fn = exp.fixed_value();
6308                }
6309                if (!(fn instanceof AST_Defun || fn instanceof AST_Function)) break;
6310                if (fn.rest) break;
6311                if (fn.uses_arguments) break;
6312                if (fn === exp) {
6313                    if (fn.parent_scope !== self) break;
6314                    if (!all(fn.enclosed, function(def) {
6315                        return def.scope !== self;
6316                    })) break;
6317                }
6318                if ((fn !== exp || fn.name)
6319                    && (parent instanceof AST_ClassMethod || parent instanceof AST_ObjectMethod)
6320                    && parent.value === compressor.self()) break;
6321                if (fn.contains_this()) break;
6322                var len = fn.argnames.length;
6323                if (len > 0 && compressor.option("inline") < 2) break;
6324                if (len > self.argnames.length) break;
6325                if (!all(self.argnames, function(argname) {
6326                    return argname instanceof AST_SymbolFunarg;
6327                })) break;
6328                if (!all(call.args, function(arg) {
6329                    return !(arg instanceof AST_Spread);
6330                })) break;
6331                for (var j = 0; j < len; j++) {
6332                    var arg = call.args[j];
6333                    if (!(arg instanceof AST_SymbolRef)) break;
6334                    if (arg.definition() !== self.argnames[j].definition()) break;
6335                }
6336                if (j < len) break;
6337                for (; j < call.args.length; j++) {
6338                    if (call.args[j].has_side_effects(compressor)) break;
6339                }
6340                if (j < call.args.length) break;
6341                if (len < self.argnames.length && !compressor.drop_fargs(self, parent)) {
6342                    if (!compressor.drop_fargs(fn, call)) break;
6343                    do {
6344                        fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len));
6345                    } while (++len < self.argnames.length);
6346                }
6347                return exp;
6348            }
6349            break;
6350        }
6351        return self;
6352    });
6353
6354    var NO_MERGE = makePredicate("arguments await yield");
6355    AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
6356        if (!compressor.option("merge_vars")) return;
6357        var in_arg = [], in_try, root, segment = {}, self = this;
6358        var first = [], last = [], index = 0;
6359        var declarations = new Dictionary();
6360        var references = Object.create(null);
6361        var prev = Object.create(null);
6362        var tw = new TreeWalker(function(node, descend) {
6363            if (node instanceof AST_Assign) {
6364                var lhs = node.left;
6365                var rhs = node.right;
6366                if (lhs instanceof AST_Destructured) {
6367                    rhs.walk(tw);
6368                    walk_destructured(AST_SymbolRef, mark, lhs);
6369                    return true;
6370                }
6371                if (lazy_op[node.operator.slice(0, -1)]) {
6372                    lhs.walk(tw);
6373                    push();
6374                    rhs.walk(tw);
6375                    if (lhs instanceof AST_SymbolRef) mark(lhs);
6376                    pop();
6377                    return true;
6378                }
6379                if (lhs instanceof AST_SymbolRef) {
6380                    if (node.operator != "=") mark(lhs, true);
6381                    rhs.walk(tw);
6382                    mark(lhs);
6383                    return true;
6384                }
6385                return;
6386            }
6387            if (node instanceof AST_Binary) {
6388                if (!lazy_op[node.operator]) return;
6389                walk_cond(node);
6390                return true;
6391            }
6392            if (node instanceof AST_Break) {
6393                var target = tw.loopcontrol_target(node);
6394                if (!(target instanceof AST_IterationStatement)) insert(target);
6395                return true;
6396            }
6397            if (node instanceof AST_Call) {
6398                var exp = node.expression;
6399                if (exp instanceof AST_LambdaExpression) {
6400                    node.args.forEach(function(arg) {
6401                        arg.walk(tw);
6402                    });
6403                    exp.walk(tw);
6404                } else {
6405                    descend();
6406                    mark_expression(exp);
6407                }
6408                return true;
6409            }
6410            if (node instanceof AST_Class) {
6411                if (node.name) node.name.walk(tw);
6412                if (node.extends) node.extends.walk(tw);
6413                node.properties.filter(function(prop) {
6414                    if (prop.key instanceof AST_Node) prop.key.walk(tw);
6415                    return prop.value;
6416                }).forEach(function(prop) {
6417                    if (prop.static) {
6418                        prop.value.walk(tw);
6419                    } else {
6420                        push();
6421                        segment.block = node;
6422                        prop.value.walk(tw);
6423                        pop();
6424                    }
6425                });
6426                return true;
6427            }
6428            if (node instanceof AST_Conditional) {
6429                walk_cond(node.condition, node.consequent, node.alternative);
6430                return true;
6431            }
6432            if (node instanceof AST_Continue) {
6433                var target = tw.loopcontrol_target(node);
6434                if (target instanceof AST_Do) insert(target);
6435                return true;
6436            }
6437            if (node instanceof AST_Do) {
6438                push();
6439                segment.block = node;
6440                segment.loop = true;
6441                var save = segment;
6442                node.body.walk(tw);
6443                if (segment.inserted === node) segment = save;
6444                node.condition.walk(tw);
6445                pop();
6446                return true;
6447            }
6448            if (node instanceof AST_For) {
6449                if (node.init) node.init.walk(tw);
6450                push();
6451                segment.block = node;
6452                segment.loop = true;
6453                if (node.condition) node.condition.walk(tw);
6454                node.body.walk(tw);
6455                if (node.step) node.step.walk(tw);
6456                pop();
6457                return true;
6458            }
6459            if (node instanceof AST_ForEnumeration) {
6460                node.object.walk(tw);
6461                push();
6462                segment.block = node;
6463                segment.loop = true;
6464                node.init.walk(tw);
6465                node.body.walk(tw);
6466                pop();
6467                return true;
6468            }
6469            if (node instanceof AST_If) {
6470                walk_cond(node.condition, node.body, node.alternative);
6471                return true;
6472            }
6473            if (node instanceof AST_LabeledStatement) {
6474                push();
6475                segment.block = node;
6476                var save = segment;
6477                node.body.walk(tw);
6478                if (segment.inserted === node) segment = save;
6479                pop();
6480                return true;
6481            }
6482            if (node instanceof AST_Scope) {
6483                push();
6484                segment.block = node;
6485                if (node === self) root = segment;
6486                if (node instanceof AST_Lambda) {
6487                    if (node.name) references[node.name.definition().id] = false;
6488                    var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) {
6489                        references[node.definition().id] = false;
6490                    } : function(node) {
6491                        mark(node);
6492                    };
6493                    in_arg.push(node);
6494                    node.argnames.forEach(function(argname) {
6495                        walk_destructured(AST_SymbolFunarg, marker, argname);
6496                    });
6497                    if (node.rest) walk_destructured(AST_SymbolFunarg, marker, node.rest);
6498                    in_arg.pop();
6499                }
6500                walk_lambda(node, tw);
6501                pop();
6502                return true;
6503            }
6504            if (node instanceof AST_Sub) {
6505                var exp = node.expression;
6506                if (node.optional) {
6507                    exp.walk(tw);
6508                    push();
6509                    node.property.walk(tw);
6510                    pop();
6511                } else {
6512                    descend();
6513                }
6514                mark_expression(exp);
6515                return true;
6516            }
6517            if (node instanceof AST_Switch) {
6518                node.expression.walk(tw);
6519                var save = segment;
6520                node.body.forEach(function(branch) {
6521                    if (branch instanceof AST_Default) return;
6522                    branch.expression.walk(tw);
6523                    if (save === segment) push();
6524                });
6525                segment = save;
6526                node.body.forEach(function(branch) {
6527                    push();
6528                    segment.block = node;
6529                    var save = segment;
6530                    walk_body(branch, tw);
6531                    if (segment.inserted === node) segment = save;
6532                    pop();
6533                });
6534                return true;
6535            }
6536            if (node instanceof AST_SymbolConst || node instanceof AST_SymbolLet) {
6537                references[node.definition().id] = false;
6538                return true;
6539            }
6540            if (node instanceof AST_SymbolRef) {
6541                mark(node, true);
6542                return true;
6543            }
6544            if (node instanceof AST_Try) {
6545                var save_try = in_try;
6546                in_try = node;
6547                walk_body(node, tw);
6548                if (node.bcatch) {
6549                    if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
6550                        if (node instanceof AST_SymbolCatch) {
6551                            var def = node.definition();
6552                            references[def.id] = false;
6553                            if (def = def.redefined()) references[def.id] = false;
6554                        }
6555                    }, tw);
6556                    if (node.bfinally || (in_try = save_try)) {
6557                        walk_body(node.bcatch, tw);
6558                    } else {
6559                        push();
6560                        walk_body(node.bcatch, tw);
6561                        pop();
6562                    }
6563                }
6564                in_try = save_try;
6565                if (node.bfinally) node.bfinally.walk(tw);
6566                return true;
6567            }
6568            if (node instanceof AST_Unary) {
6569                if (!UNARY_POSTFIX[node.operator]) return;
6570                var sym = node.expression;
6571                if (!(sym instanceof AST_SymbolRef)) return;
6572                mark(sym, true);
6573                return true;
6574            }
6575            if (node instanceof AST_VarDef) {
6576                var assigned = node.value;
6577                if (assigned) {
6578                    assigned.walk(tw);
6579                } else {
6580                    assigned = segment.block instanceof AST_ForEnumeration && segment.block.init === tw.parent();
6581                }
6582                walk_destructured(AST_SymbolDeclaration, assigned ? function(node) {
6583                    if (node instanceof AST_SymbolVar) {
6584                        mark(node);
6585                    } else {
6586                        node.walk(tw);
6587                    }
6588                } : function(node) {
6589                    if (node instanceof AST_SymbolVar) {
6590                        var id = node.definition().id;
6591                        var refs = references[id];
6592                        if (refs) {
6593                            refs.push(node);
6594                        } else if (!(id in references)) {
6595                            declarations.add(id, node);
6596                        }
6597                    } else {
6598                        node.walk(tw);
6599                    }
6600                }, node.name);
6601                return true;
6602            }
6603            if (node instanceof AST_While) {
6604                push();
6605                segment.block = node;
6606                segment.loop = true;
6607                descend();
6608                pop();
6609                return true;
6610            }
6611
6612            function mark_expression(exp) {
6613                if (!compressor.option("ie")) return;
6614                var sym = root_expr(exp);
6615                if (sym instanceof AST_SymbolRef) sym.walk(tw);
6616            }
6617
6618            function walk_cond(condition, consequent, alternative) {
6619                var save = segment;
6620                var segments = [ save, save ];
6621                if (condition instanceof AST_Binary) switch (condition.operator) {
6622                  case "&&":
6623                    segments[0] = walk_cond(condition.left, condition.right)[0];
6624                    break;
6625                  case "||":
6626                  case "??":
6627                    segments[1] = walk_cond(condition.left, null, condition.right)[1];
6628                    break;
6629                  default:
6630                    condition.walk(tw);
6631                    break;
6632                } else if (condition instanceof AST_Conditional) {
6633                    walk_cond(condition.condition, condition.consequent, condition.alternative);
6634                } else {
6635                    condition.walk(tw);
6636                }
6637                segment = segments[0];
6638                if (consequent) {
6639                    push();
6640                    consequent.walk(tw);
6641                }
6642                segments[0] = segment;
6643                segment = segments[1];
6644                if (alternative) {
6645                    push();
6646                    alternative.walk(tw);
6647                }
6648                segments[1] = segment;
6649                segment = save;
6650                return segments;
6651            }
6652        });
6653        tw.directives = Object.create(compressor.directives);
6654        self.walk(tw);
6655        var changed = false;
6656        var merged = Object.create(null);
6657        while (first.length && last.length) {
6658            var tail = last.shift();
6659            if (!tail) continue;
6660            var def = tail.definition;
6661            var tail_refs = references[def.id];
6662            if (!tail_refs) continue;
6663            tail_refs = { end: tail_refs.end };
6664            while (def.id in merged) def = merged[def.id];
6665            tail_refs.start = references[def.id].start;
6666            var skipped = [];
6667            do {
6668                var head = first.shift();
6669                if (tail.index > head.index) continue;
6670                var prev_def = head.definition;
6671                if (!(prev_def.id in prev)) continue;
6672                var head_refs = references[prev_def.id];
6673                if (!head_refs) continue;
6674                if (head_refs.start.block !== tail_refs.start.block
6675                    || !mergeable(head_refs, tail_refs)
6676                    || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs)
6677                    || compressor.option("webkit") && is_funarg(def) !== is_funarg(prev_def)
6678                    || prev_def.const_redefs
6679                    || !all(head_refs.scopes, function(scope) {
6680                        return scope.find_variable(def.name) === def;
6681                    })) {
6682                    skipped.push(head);
6683                    continue;
6684                }
6685                head_refs.forEach(function(sym) {
6686                    sym.thedef = def;
6687                    sym.name = def.name;
6688                    if (sym instanceof AST_SymbolRef) {
6689                        def.references.push(sym);
6690                        prev_def.replaced++;
6691                    } else {
6692                        def.orig.push(sym);
6693                        prev_def.eliminated++;
6694                    }
6695                });
6696                if (!prev_def.fixed) def.fixed = false;
6697                merged[prev_def.id] = def;
6698                changed = true;
6699                break;
6700            } while (first.length);
6701            if (skipped.length) first = skipped.concat(first);
6702        }
6703        return changed;
6704
6705        function push() {
6706            segment = Object.create(segment);
6707        }
6708
6709        function pop() {
6710            segment = Object.getPrototypeOf(segment);
6711        }
6712
6713        function walk_destructured(symbol_type, mark, lhs) {
6714            var marker = new TreeWalker(function(node) {
6715                if (node instanceof AST_Destructured) return;
6716                if (node instanceof AST_DefaultValue) {
6717                    push();
6718                    node.value.walk(tw);
6719                    pop();
6720                    node.name.walk(marker);
6721                } else if (node instanceof AST_DestructuredKeyVal) {
6722                    if (!(node.key instanceof AST_Node)) {
6723                        node.value.walk(marker);
6724                    } else if (node.value instanceof AST_PropAccess) {
6725                        push();
6726                        segment.block = node;
6727                        node.key.walk(tw);
6728                        node.value.walk(marker);
6729                        pop();
6730                    } else {
6731                        node.key.walk(tw);
6732                        node.value.walk(marker);
6733                    }
6734                } else if (node instanceof symbol_type) {
6735                    mark(node);
6736                } else {
6737                    node.walk(tw);
6738                }
6739                return true;
6740            });
6741            lhs.walk(marker);
6742        }
6743
6744        function mark(sym, read) {
6745            var def = sym.definition(), ldef;
6746            if (read && !all(in_arg, function(fn) {
6747                ldef = fn.variables.get(sym.name);
6748                if (!ldef) return true;
6749                if (!is_funarg(ldef)) return true;
6750                return ldef !== def
6751                    && !def.undeclared
6752                    && fn.parent_scope.find_variable(sym.name) !== def;
6753            })) return references[def.id] = references[ldef.id] = false;
6754            var seg = segment;
6755            if (in_try) {
6756                push();
6757                seg = segment;
6758                pop();
6759            }
6760            if (def.id in references) {
6761                var refs = references[def.id];
6762                if (!refs) return;
6763                if (refs.start.block !== seg.block) return references[def.id] = false;
6764                push_ref(sym);
6765                refs.end = seg;
6766                if (def.id in prev) {
6767                    last[prev[def.id]] = null;
6768                } else if (!read) {
6769                    return;
6770                }
6771            } else if ((ldef = self.variables.get(def.name)) !== def) {
6772                if (ldef && root === seg) references[ldef.id] = false;
6773                return references[def.id] = false;
6774            } else if (compressor.exposed(def) || NO_MERGE[sym.name]) {
6775                return references[def.id] = false;
6776            } else {
6777                var refs = declarations.get(def.id) || [];
6778                refs.scopes = [];
6779                push_ref(sym);
6780                references[def.id] = refs;
6781                if (!read) {
6782                    refs.start = seg;
6783                    return first.push({
6784                        index: index++,
6785                        definition: def,
6786                    });
6787                }
6788                if (seg.block !== self) return references[def.id] = false;
6789                refs.start = root;
6790            }
6791            prev[def.id] = last.length;
6792            last.push({
6793                index: index++,
6794                definition: def,
6795            });
6796
6797            function push_ref(sym) {
6798                refs.push(sym);
6799                push_uniq(refs.scopes, sym.scope);
6800                var scope = find_scope(tw);
6801                if (scope !== sym.scope) push_uniq(refs.scopes, scope);
6802            }
6803        }
6804
6805        function insert(target) {
6806            var stack = [];
6807            while (true) {
6808                if (HOP(segment, "block")) {
6809                    var block = segment.block;
6810                    if (block instanceof AST_LabeledStatement) block = block.body;
6811                    if (block === target) break;
6812                }
6813                stack.push(segment);
6814                pop();
6815            }
6816            segment.inserted = segment.block;
6817            push();
6818            while (stack.length) {
6819                var seg = stack.pop();
6820                push();
6821                if (HOP(seg, "block")) segment.block = seg.block;
6822                if (HOP(seg, "loop")) segment.loop = seg.loop;
6823            }
6824        }
6825
6826        function must_visit(base, segment) {
6827            return base === segment || base.isPrototypeOf(segment);
6828        }
6829
6830        function mergeable(head, tail) {
6831            return must_visit(head.start, head.end) || must_visit(head.start, tail.start);
6832        }
6833    });
6834
6835    function fill_holes(orig, elements) {
6836        for (var i = elements.length; --i >= 0;) {
6837            if (!elements[i]) elements[i] = make_node(AST_Hole, orig);
6838        }
6839    }
6840
6841    function to_class_expr(defcl, drop_name) {
6842        var cl = make_node(AST_ClassExpression, defcl);
6843        if (cl.name) cl.name = drop_name ? null : make_node(AST_SymbolClass, cl.name);
6844        return cl;
6845    }
6846
6847    function to_func_expr(defun, drop_name) {
6848        var ctor;
6849        switch (defun.CTOR) {
6850          case AST_AsyncDefun:
6851            ctor = AST_AsyncFunction;
6852            break;
6853          case AST_AsyncGeneratorDefun:
6854            ctor = AST_AsyncGeneratorFunction;
6855            break;
6856          case AST_Defun:
6857            ctor = AST_Function;
6858            break;
6859          case AST_GeneratorDefun:
6860            ctor = AST_GeneratorFunction;
6861            break;
6862        }
6863        var fn = make_node(ctor, defun);
6864        fn.name = drop_name ? null : make_node(AST_SymbolLambda, defun.name);
6865        return fn;
6866    }
6867
6868    AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
6869        if (!compressor.option("unused")) return;
6870        var self = this;
6871        var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
6872        var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
6873        var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
6874            var sym, nested = false;
6875            if (node instanceof AST_Assign) {
6876                if (node.write_only || node.operator == "=") sym = extract_reference(node.left, props);
6877            } else if (node instanceof AST_Unary) {
6878                if (node.write_only) sym = extract_reference(node.expression, props);
6879            }
6880            if (!(sym instanceof AST_SymbolRef)) return;
6881            var def = sym.definition();
6882            if (export_defaults[def.id]) return;
6883            if (compressor.exposed(def)) return;
6884            if (!can_drop_symbol(sym, compressor, nested)) return;
6885            return sym;
6886
6887            function extract_reference(node, props) {
6888                if (node instanceof AST_PropAccess) {
6889                    var expr = node.expression;
6890                    if (!expr.may_throw_on_access(compressor, true)) {
6891                        nested = true;
6892                        if (props && node instanceof AST_Sub) props.unshift(node.property);
6893                        return extract_reference(expr, props);
6894                    }
6895                } else if (node instanceof AST_Assign && node.operator == "=") {
6896                    node.write_only = "p";
6897                    var ref = extract_reference(node.right);
6898                    if (!props) return ref;
6899                    props.assign = node;
6900                    return ref instanceof AST_SymbolRef ? ref : node.left;
6901                }
6902                return node;
6903            }
6904        };
6905        var assign_in_use = Object.create(null);
6906        var export_defaults = Object.create(null);
6907        var find_variable = function(name) {
6908            find_variable = compose(self, 0, noop);
6909            return find_variable(name);
6910
6911            function compose(child, level, find) {
6912                var parent = compressor.parent(level);
6913                if (!parent) return find;
6914                var in_arg = parent instanceof AST_Lambda && member(child, parent.argnames);
6915                return compose(parent, level + 1, in_arg ? function(name) {
6916                    var def = find(name);
6917                    if (def) return def;
6918                    def = parent.variables.get(name);
6919                    if (def) {
6920                        var sym = def.orig[0];
6921                        if (sym instanceof AST_SymbolFunarg || sym instanceof AST_SymbolLambda) return def;
6922                    }
6923                } : parent.variables ? function(name) {
6924                    return find(name) || parent.variables.get(name);
6925                } : find);
6926            }
6927        };
6928        var for_ins = Object.create(null);
6929        var in_use = [];
6930        var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
6931        var lambda_ids = Object.create(null);
6932        var value_read = Object.create(null);
6933        var value_modified = Object.create(null);
6934        var var_defs = Object.create(null);
6935        if (self instanceof AST_Toplevel && compressor.top_retain) {
6936            self.variables.each(function(def) {
6937                if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
6938                    AST_Node.info("Retaining variable {name}", def);
6939                    in_use_ids[def.id] = true;
6940                    in_use.push(def);
6941                }
6942            });
6943        }
6944        var assignments = new Dictionary();
6945        var initializations = new Dictionary();
6946        // pass 1: find out which symbols are directly used in
6947        // this scope (not in nested scopes).
6948        var scope = this;
6949        var tw = new TreeWalker(function(node, descend) {
6950            if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) {
6951                node.each_argname(function(argname) {
6952                    var def = argname.definition();
6953                    if (!(def.id in in_use_ids)) {
6954                        in_use_ids[def.id] = true;
6955                        in_use.push(def);
6956                    }
6957                });
6958            }
6959            if (node === self) return;
6960            if (scope === self) {
6961                if (node instanceof AST_DefClass) {
6962                    var def = node.name.definition();
6963                    var drop = drop_funcs && !def.exported;
6964                    if (!drop && !(def.id in in_use_ids)) {
6965                        in_use_ids[def.id] = true;
6966                        in_use.push(def);
6967                    }
6968                    var used = tw.parent() instanceof AST_ExportDefault;
6969                    if (used) {
6970                        export_defaults[def.id] = true;
6971                    } else if (drop && !(def.id in lambda_ids)) {
6972                        lambda_ids[def.id] = 1;
6973                    }
6974                    if (node.extends) node.extends.walk(tw);
6975                    var values = [];
6976                    node.properties.forEach(function(prop) {
6977                        if (prop.key instanceof AST_Node) prop.key.walk(tw);
6978                        var value = prop.value;
6979                        if (!value) return;
6980                        if (is_static_field_or_init(prop)) {
6981                            if (!used && value.contains_this()) used = true;
6982                            walk_class_prop(value);
6983                        } else {
6984                            values.push(value);
6985                        }
6986                    });
6987                    values.forEach(drop && used ? walk_class_prop : function(value) {
6988                        initializations.add(def.id, value);
6989                    });
6990                    return true;
6991                }
6992                if (node instanceof AST_LambdaDefinition) {
6993                    var def = node.name.definition();
6994                    var drop = drop_funcs && !def.exported;
6995                    if (!drop && !(def.id in in_use_ids)) {
6996                        in_use_ids[def.id] = true;
6997                        in_use.push(def);
6998                    }
6999                    initializations.add(def.id, node);
7000                    if (tw.parent() instanceof AST_ExportDefault) {
7001                        export_defaults[def.id] = true;
7002                        return scan_ref_scoped(node, descend, true);
7003                    }
7004                    if (drop && !(def.id in lambda_ids)) lambda_ids[def.id] = 1;
7005                    return true;
7006                }
7007                if (node instanceof AST_Definitions) {
7008                    node.definitions.forEach(function(defn) {
7009                        var value = defn.value;
7010                        var side_effects = value
7011                            && (defn.name instanceof AST_Destructured || value.has_side_effects(compressor));
7012                        var shared = side_effects && value.tail_node().operator == "=";
7013                        defn.name.mark_symbol(function(name) {
7014                            if (!(name instanceof AST_SymbolDeclaration)) return;
7015                            var def = name.definition();
7016                            var_defs[def.id] = (var_defs[def.id] || 0) + 1;
7017                            if (node instanceof AST_Var && def.orig[0] instanceof AST_SymbolCatch) {
7018                                var redef = def.redefined();
7019                                if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1;
7020                            }
7021                            if (!(def.id in in_use_ids) && (!drop_vars || def.exported
7022                                || (node instanceof AST_Const ? def.redefined() : def.const_redefs)
7023                                || !(node instanceof AST_Var || is_safe_lexical(def)))) {
7024                                in_use_ids[def.id] = true;
7025                                in_use.push(def);
7026                            }
7027                            if (value) {
7028                                if (!side_effects) {
7029                                    initializations.add(def.id, value);
7030                                } else if (shared) {
7031                                    verify_safe_usage(def, name, value_modified[def.id]);
7032                                }
7033                                assignments.add(def.id, defn);
7034                            }
7035                            unmark_lambda(def);
7036                            return true;
7037                        }, tw);
7038                        if (side_effects) value.walk(tw);
7039                    });
7040                    return true;
7041                }
7042                if (node instanceof AST_SymbolFunarg) {
7043                    var def = node.definition();
7044                    var_defs[def.id] = (var_defs[def.id] || 0) + 1;
7045                    assignments.add(def.id, node);
7046                    return true;
7047                }
7048                if (node instanceof AST_SymbolImport) {
7049                    var def = node.definition();
7050                    if (!(def.id in in_use_ids) && (!drop_vars || !is_safe_lexical(def))) {
7051                        in_use_ids[def.id] = true;
7052                        in_use.push(def);
7053                    }
7054                    return true;
7055                }
7056            }
7057            return scan_ref_scoped(node, descend, true);
7058
7059            function walk_class_prop(value) {
7060                var save_scope = scope;
7061                scope = node;
7062                value.walk(tw);
7063                scope = save_scope;
7064            }
7065        });
7066        tw.directives = Object.create(compressor.directives);
7067        self.walk(tw);
7068        var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie") ? function(def) {
7069            return !compressor.exposed(def) && def.references.length == def.replaced;
7070        } : function(def) {
7071            if (!(def.id in in_use_ids)) return true;
7072            if (def.orig.length - def.eliminated < 2) return false;
7073            // function argument will always overshadow its name
7074            if (def.orig[1] instanceof AST_SymbolFunarg) return true;
7075            // retain if referenced within destructured object of argument
7076            return all(def.references, function(ref) {
7077                return !ref.in_arg;
7078            });
7079        };
7080        if (compressor.option("ie")) initializations.each(function(init, id) {
7081            if (id in in_use_ids) return;
7082            init.forEach(function(init) {
7083                init.walk(new TreeWalker(function(node) {
7084                    if (node instanceof AST_Function && node.name && !drop_fn_name(node.name.definition())) {
7085                        node.walk(tw);
7086                        return true;
7087                    }
7088                    if (node instanceof AST_Scope) return true;
7089                }));
7090            });
7091        });
7092        // pass 2: for every used symbol we need to walk its
7093        // initialization code to figure out if it uses other
7094        // symbols (that may not be in_use).
7095        tw = new TreeWalker(scan_ref_scoped);
7096        for (var i = 0; i < in_use.length; i++) {
7097            var init = initializations.get(in_use[i].id);
7098            if (init) init.forEach(function(init) {
7099                init.walk(tw);
7100            });
7101        }
7102        Object.keys(assign_in_use).forEach(function(id) {
7103            var assigns = assign_in_use[id];
7104            if (!assigns) {
7105                delete assign_in_use[id];
7106                return;
7107            }
7108            assigns = assigns.reduce(function(in_use, assigns) {
7109                assigns.forEach(function(assign) {
7110                    push_uniq(in_use, assign);
7111                });
7112                return in_use;
7113            }, []);
7114            var in_use = (assignments.get(id) || []).filter(function(node) {
7115                return find_if(node instanceof AST_Unary ? function(assign) {
7116                    return assign === node;
7117                } : function(assign) {
7118                    if (assign === node) return true;
7119                    if (assign instanceof AST_Unary) return false;
7120                    return get_rvalue(assign) === get_rvalue(node);
7121                }, assigns);
7122            });
7123            if (assigns.length == in_use.length) {
7124                assign_in_use[id] = in_use;
7125            } else {
7126                delete assign_in_use[id];
7127            }
7128        });
7129        // pass 3: we should drop declarations not in_use
7130        var calls_to_drop_args = [];
7131        var fns_with_marked_args = [];
7132        var trimmer = new TreeTransformer(function(node) {
7133            if (node instanceof AST_DefaultValue) return trim_default(trimmer, node);
7134            if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer);
7135            if (node instanceof AST_DestructuredArray) {
7136                var trim = !node.rest;
7137                for (var i = node.elements.length; --i >= 0;) {
7138                    var element = node.elements[i].transform(trimmer);
7139                    if (element) {
7140                        node.elements[i] = element;
7141                        trim = false;
7142                    } else if (trim) {
7143                        node.elements.pop();
7144                    } else {
7145                        node.elements[i] = make_node(AST_Hole, node.elements[i]);
7146                    }
7147                }
7148                return node;
7149            }
7150            if (node instanceof AST_DestructuredObject) {
7151                var properties = [];
7152                node.properties.forEach(function(prop) {
7153                    var retain = false;
7154                    if (prop.key instanceof AST_Node) {
7155                        prop.key = prop.key.transform(tt);
7156                        retain = prop.key.has_side_effects(compressor);
7157                    }
7158                    if ((retain || node.rest) && is_decl(prop.value)) {
7159                        prop.value = prop.value.transform(tt);
7160                        properties.push(prop);
7161                    } else {
7162                        var value = prop.value.transform(trimmer);
7163                        if (!value && node.rest) {
7164                            if (prop.value instanceof AST_DestructuredArray) {
7165                                value = make_node(AST_DestructuredArray, prop.value, { elements: [] });
7166                            } else {
7167                                value = make_node(AST_DestructuredObject, prop.value, { properties: [] });
7168                            }
7169                        }
7170                        if (value) {
7171                            prop.value = value;
7172                            properties.push(prop);
7173                        }
7174                    }
7175                });
7176                node.properties = properties;
7177                return node;
7178            }
7179            if (node instanceof AST_SymbolDeclaration) return trim_decl(node);
7180        });
7181        var tt = new TreeTransformer(function(node, descend, in_list) {
7182            var parent = tt.parent();
7183            if (drop_vars) {
7184                var props = [], sym = assign_as_unused(node, props);
7185                if (sym) {
7186                    var value;
7187                    if (can_drop_lhs(sym, node)) {
7188                        if (node instanceof AST_Assign) {
7189                            value = get_rhs(node);
7190                            if (node.write_only === true) value = value.drop_side_effect_free(compressor);
7191                        }
7192                        if (!value) value = make_node(AST_Number, node, { value: 0 });
7193                    }
7194                    if (value) {
7195                        if (props.assign) {
7196                            var assign = props.assign.drop_side_effect_free(compressor);
7197                            if (assign) {
7198                                assign.write_only = true;
7199                                props.unshift(assign);
7200                            }
7201                        }
7202                        if (!(parent instanceof AST_Sequence)
7203                            || parent.tail_node() === node
7204                            || value.has_side_effects(compressor)) {
7205                            props.push(value);
7206                        }
7207                        switch (props.length) {
7208                          case 0:
7209                            return List.skip;
7210                          case 1:
7211                            return maintain_this_binding(parent, node, props[0].transform(tt));
7212                          default:
7213                            return make_sequence(node, props.map(function(prop) {
7214                                return prop.transform(tt);
7215                            }));
7216                        }
7217                    }
7218                } else if (node instanceof AST_UnaryPostfix
7219                    && node.expression instanceof AST_SymbolRef
7220                    && indexOf_assign(node.expression.definition(), node) < 0) {
7221                    return make_node(AST_UnaryPrefix, node, {
7222                        operator: "+",
7223                        expression: node.expression,
7224                    });
7225                }
7226            }
7227            if (node instanceof AST_Binary && node.operator == "instanceof") {
7228                var sym = node.right;
7229                if (!(sym instanceof AST_SymbolRef)) return;
7230                if (sym.definition().id in in_use_ids) return;
7231                var lhs = node.left.drop_side_effect_free(compressor);
7232                var value = make_node(AST_False, node).optimize(compressor);
7233                return lhs ? make_sequence(node, [ lhs, value ]) : value;
7234            }
7235            if (node instanceof AST_Call) {
7236                calls_to_drop_args.push(node);
7237                node.args = node.args.map(function(arg) {
7238                    return arg.transform(tt);
7239                });
7240                node.expression = node.expression.transform(tt);
7241                return node;
7242            }
7243            if (scope !== self) return;
7244            if (drop_funcs && node !== self && node instanceof AST_DefClass) {
7245                var def = node.name.definition();
7246                if (!(def.id in in_use_ids)) {
7247                    log(node.name, "Dropping unused class {name}");
7248                    def.eliminated++;
7249                    descend(node, tt);
7250                    var trimmed = to_class_expr(node, true);
7251                    if (parent instanceof AST_ExportDefault) return trimmed;
7252                    trimmed = trimmed.drop_side_effect_free(compressor, true);
7253                    if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed });
7254                    return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7255                }
7256            }
7257            if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) {
7258                node.name = null;
7259            }
7260            if (node instanceof AST_Lambda) {
7261                if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
7262                    var def = node.name.definition();
7263                    if (!(def.id in in_use_ids)) {
7264                        log(node.name, "Dropping unused function {name}");
7265                        def.eliminated++;
7266                        if (parent instanceof AST_ExportDefault) {
7267                            descend_scope();
7268                            return to_func_expr(node, true);
7269                        }
7270                        return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7271                    }
7272                }
7273                descend_scope();
7274                if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
7275                    node.name = null;
7276                }
7277                if (!(node instanceof AST_Accessor)) {
7278                    var args, spread, trim = compressor.drop_fargs(node, parent);
7279                    if (trim && parent instanceof AST_Call && parent.expression === node) {
7280                        args = parent.args;
7281                        for (spread = 0; spread < args.length; spread++) {
7282                            if (args[spread] instanceof AST_Spread) break;
7283                        }
7284                    }
7285                    var argnames = node.argnames;
7286                    var rest = node.rest;
7287                    var after = false, before = false;
7288                    if (rest) {
7289                        before = true;
7290                        if (!args || spread < argnames.length || rest instanceof AST_SymbolFunarg) {
7291                            rest = rest.transform(trimmer);
7292                        } else {
7293                            var trimmed = trim_destructured(rest, make_node(AST_Array, parent, {
7294                                elements: args.slice(argnames.length),
7295                            }), trim_decl, !node.uses_arguments, rest);
7296                            rest = trimmed.name;
7297                            args.length = argnames.length;
7298                            if (trimmed.value.elements.length) [].push.apply(args, trimmed.value.elements);
7299                        }
7300                        if (rest instanceof AST_Destructured && !rest.rest) {
7301                            if (rest instanceof AST_DestructuredArray) {
7302                                if (rest.elements.length == 0) rest = null;
7303                            } else if (rest.properties.length == 0) {
7304                                rest = null;
7305                            }
7306                        }
7307                        node.rest = rest;
7308                        if (rest) {
7309                            trim = false;
7310                            after = true;
7311                        }
7312                    }
7313                    var default_length = trim ? -1 : node.length();
7314                    var trim_value = args && !node.uses_arguments && parent !== compressor.parent();
7315                    for (var i = argnames.length; --i >= 0;) {
7316                        var sym = argnames[i];
7317                        if (sym instanceof AST_SymbolFunarg) {
7318                            var def = sym.definition();
7319                            if (def.id in in_use_ids) {
7320                                trim = false;
7321                                if (indexOf_assign(def, sym) < 0) sym.unused = null;
7322                            } else if (trim) {
7323                                log(sym, "Dropping unused function argument {name}");
7324                                argnames.pop();
7325                                def.eliminated++;
7326                                sym.unused = true;
7327                            } else {
7328                                sym.unused = true;
7329                            }
7330                        } else {
7331                            before = true;
7332                            var funarg;
7333                            if (!args || spread < i) {
7334                                funarg = sym.transform(trimmer);
7335                            } else {
7336                                var trimmed = trim_destructured(sym, args[i], trim_decl, trim_value, sym);
7337                                funarg = trimmed.name;
7338                                if (trimmed.value) args[i] = trimmed.value;
7339                            }
7340                            if (funarg) {
7341                                trim = false;
7342                                argnames[i] = funarg;
7343                                if (!after) after = !(funarg instanceof AST_SymbolFunarg);
7344                            } else if (trim) {
7345                                log_default(sym, "Dropping unused default argument {name}");
7346                                argnames.pop();
7347                            } else if (i > default_length) {
7348                                log_default(sym, "Dropping unused default argument assignment {name}");
7349                                if (sym.name instanceof AST_SymbolFunarg) {
7350                                    sym.name.unused = true;
7351                                } else {
7352                                    after = true;
7353                                }
7354                                argnames[i] = sym.name;
7355                            } else {
7356                                log_default(sym, "Dropping unused default argument value {name}");
7357                                argnames[i] = sym = sym.clone();
7358                                sym.value = make_node(AST_Number, sym, { value: 0 });
7359                                after = true;
7360                            }
7361                        }
7362                    }
7363                    if (before && !after && node.uses_arguments && !tt.has_directive("use strict")) {
7364                        node.rest = make_node(AST_DestructuredArray, node, { elements: [] });
7365                    }
7366                    fns_with_marked_args.push(node);
7367                }
7368                return node;
7369            }
7370            if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
7371                node.argname.transform(trimmer);
7372            }
7373            if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) {
7374                // place uninitialized names at the start
7375                var body = [], head = [], tail = [];
7376                // for unused names whose initialization has
7377                // side effects, we can cascade the init. code
7378                // into the next one, or next statement.
7379                var side_effects = [];
7380                var duplicated = 0;
7381                var is_var = node instanceof AST_Var;
7382                node.definitions.forEach(function(def) {
7383                    if (def.value) def.value = def.value.transform(tt);
7384                    var value = def.value;
7385                    if (def.name instanceof AST_Destructured) {
7386                        var trimmed = trim_destructured(def.name, value, function(node) {
7387                            if (!drop_vars) return node;
7388                            if (node.definition().id in in_use_ids) return node;
7389                            if (is_catch(node)) return node;
7390                            if (is_var && !can_drop_symbol(node)) return node;
7391                            return null;
7392                        }, true);
7393                        if (trimmed.name) {
7394                            def = make_node(AST_VarDef, def, {
7395                                name: trimmed.name,
7396                                value: value = trimmed.value,
7397                            });
7398                            flush();
7399                        } else if (trimmed.value) {
7400                            side_effects.push(trimmed.value);
7401                        }
7402                        return;
7403                    }
7404                    var sym = def.name.definition();
7405                    var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym);
7406                    if (!drop_sym || !drop_vars || sym.id in in_use_ids) {
7407                        var index;
7408                        if (value && ((index = indexOf_assign(sym, def)) < 0 || self_assign(value.tail_node()))) {
7409                            def = def.clone();
7410                            value = value.drop_side_effect_free(compressor);
7411                            if (value) AST_Node.warn("Side effects in definition of variable {name} [{start}]", def.name);
7412                            if (node instanceof AST_Const) {
7413                                def.value = value || make_node(AST_Number, def, { value: 0 });
7414                            } else {
7415                                def.value = null;
7416                                if (value) side_effects.push(value);
7417                            }
7418                            value = null;
7419                            if (index >= 0) assign_in_use[sym.id][index] = def;
7420                        }
7421                        var old_def, fn;
7422                        if (!value && !(node instanceof AST_Let)) {
7423                            if (parent instanceof AST_ExportDeclaration) {
7424                                flush();
7425                            } else if (drop_sym && var_defs[sym.id] > 1) {
7426                                AST_Node.info("Dropping declaration of variable {name} [{start}]", def.name);
7427                                var_defs[sym.id]--;
7428                                sym.eliminated++;
7429                            } else {
7430                                head.push(def);
7431                            }
7432                        } else if (compressor.option("functions")
7433                            && !compressor.option("ie")
7434                            && drop_sym
7435                            && value
7436                            && var_defs[sym.id] == 1
7437                            && sym.assignments == 0
7438                            && (fn = value.tail_node()) instanceof AST_LambdaExpression
7439                            && !is_arguments(sym)
7440                            && !is_arrow(fn)
7441                            && assigned_once(fn, sym.references)
7442                            && can_declare_defun(fn)
7443                            && (old_def = rename_def(fn, def.name.name)) !== false) {
7444                            AST_Node.warn("Declaring {name} as function [{start}]", def.name);
7445                            var ctor;
7446                            switch (fn.CTOR) {
7447                              case AST_AsyncFunction:
7448                                ctor = AST_AsyncDefun;
7449                                break;
7450                              case AST_AsyncGeneratorFunction:
7451                                ctor = AST_AsyncGeneratorDefun;
7452                                break;
7453                              case AST_Function:
7454                                ctor = AST_Defun;
7455                                break;
7456                              case AST_GeneratorFunction:
7457                                ctor = AST_GeneratorDefun;
7458                                break;
7459                            }
7460                            var defun = make_node(ctor, fn);
7461                            defun.name = make_node(AST_SymbolDefun, def.name);
7462                            var name_def = def.name.scope.resolve().def_function(defun.name);
7463                            if (old_def) old_def.forEach(function(node) {
7464                                node.name = name_def.name;
7465                                node.thedef = name_def;
7466                                node.reference();
7467                            });
7468                            body.push(defun);
7469                            if (value !== fn) [].push.apply(side_effects, value.expressions.slice(0, -1));
7470                        } else {
7471                            if (drop_sym
7472                                && var_defs[sym.id] > 1
7473                                && !(parent instanceof AST_ExportDeclaration)
7474                                && sym.orig.indexOf(def.name) > sym.eliminated) {
7475                                var_defs[sym.id]--;
7476                                duplicated++;
7477                            }
7478                            flush();
7479                        }
7480                    } else if (is_catch(def.name)) {
7481                        value = value && value.drop_side_effect_free(compressor);
7482                        if (value) side_effects.push(value);
7483                        if (var_defs[sym.id] > 1) {
7484                            AST_Node.warn("Dropping duplicated declaration of variable {name} [{start}]", def.name);
7485                            var_defs[sym.id]--;
7486                            sym.eliminated++;
7487                        } else {
7488                            def.value = null;
7489                            head.push(def);
7490                        }
7491                    } else {
7492                        value = value && value.drop_side_effect_free(compressor);
7493                        if (value) {
7494                            AST_Node.warn("Side effects in initialization of unused variable {name} [{start}]", def.name);
7495                            side_effects.push(value);
7496                        } else {
7497                            log(def.name, "Dropping unused variable {name}");
7498                        }
7499                        sym.eliminated++;
7500                    }
7501
7502                    function self_assign(ref) {
7503                        return ref instanceof AST_SymbolRef && ref.definition() === sym;
7504                    }
7505
7506                    function assigned_once(fn, refs) {
7507                        if (refs.length == 0) return fn === def.name.fixed_value();
7508                        return all(refs, function(ref) {
7509                            return fn === ref.fixed_value();
7510                        });
7511                    }
7512
7513                    function can_declare_defun(fn) {
7514                        if (!is_var || compressor.has_directive("use strict") || !(fn instanceof AST_Function)) {
7515                            return parent instanceof AST_Scope;
7516                        }
7517                        return parent instanceof AST_Block
7518                            || parent instanceof AST_For && parent.init === node
7519                            || parent instanceof AST_If;
7520                    }
7521
7522                    function rename_def(fn, name) {
7523                        if (!fn.name) return null;
7524                        var def = fn.name.definition();
7525                        if (def.orig.length > 1) return null;
7526                        if (def.assignments > 0) return false;
7527                        if (def.name == name) return def;
7528                        if (compressor.option("keep_fnames")) return false;
7529                        var forbidden;
7530                        switch (name) {
7531                          case "await":
7532                            forbidden = is_async;
7533                            break;
7534                          case "yield":
7535                            forbidden = is_generator;
7536                            break;
7537                        }
7538                        return all(def.references, function(ref) {
7539                            var scope = ref.scope;
7540                            if (scope.find_variable(name) !== sym) return false;
7541                            if (forbidden) do {
7542                                scope = scope.resolve();
7543                                if (forbidden(scope)) return false;
7544                            } while (scope !== fn && (scope = scope.parent_scope));
7545                            return true;
7546                        }) && def;
7547                    }
7548
7549                    function is_catch(node) {
7550                        var sym = node.definition();
7551                        return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve();
7552                    }
7553
7554                    function flush() {
7555                        if (side_effects.length > 0) {
7556                            if (tail.length == 0) {
7557                                body.push(make_node(AST_SimpleStatement, node, {
7558                                    body: make_sequence(node, side_effects),
7559                                }));
7560                            } else if (value) {
7561                                side_effects.push(value);
7562                                def.value = make_sequence(value, side_effects);
7563                            } else {
7564                                def.value = make_node(AST_UnaryPrefix, def, {
7565                                    operator: "void",
7566                                    expression: make_sequence(def, side_effects),
7567                                });
7568                            }
7569                            side_effects = [];
7570                        }
7571                        tail.push(def);
7572                    }
7573                });
7574                switch (head.length) {
7575                  case 0:
7576                    if (tail.length == 0) break;
7577                    if (tail.length == duplicated) {
7578                        [].unshift.apply(side_effects, tail.map(function(def) {
7579                            AST_Node.info("Dropping duplicated definition of variable {name} [{start}]", def.name);
7580                            var sym = def.name.definition();
7581                            var ref = make_node(AST_SymbolRef, def.name);
7582                            sym.references.push(ref);
7583                            var assign = make_node(AST_Assign, def, {
7584                                operator: "=",
7585                                left: ref,
7586                                right: def.value,
7587                            });
7588                            var index = indexOf_assign(sym, def);
7589                            if (index >= 0) assign_in_use[sym.id][index] = assign;
7590                            sym.assignments++;
7591                            sym.eliminated++;
7592                            return assign;
7593                        }));
7594                        break;
7595                    }
7596                  case 1:
7597                    if (tail.length == 0) {
7598                        var id = head[0].name.definition().id;
7599                        if (id in for_ins) {
7600                            node.definitions = head;
7601                            for_ins[id].init = node;
7602                            break;
7603                        }
7604                    }
7605                  default:
7606                    var seq;
7607                    if (tail.length > 0 && (seq = tail[0].value) instanceof AST_Sequence) {
7608                        tail[0].value = seq.tail_node();
7609                        body.push(make_node(AST_SimpleStatement, node, {
7610                            body: make_sequence(seq, seq.expressions.slice(0, -1)),
7611                        }));
7612                    }
7613                    node.definitions = head.concat(tail);
7614                    body.push(node);
7615                }
7616                if (side_effects.length > 0) {
7617                    body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) }));
7618                }
7619                return insert_statements(body, node, in_list);
7620            }
7621            if (node instanceof AST_Assign) {
7622                descend(node, tt);
7623                if (!(node.left instanceof AST_Destructured)) return node;
7624                var trimmed = trim_destructured(node.left, node.right, function(node) {
7625                    return node;
7626                }, node.write_only === true);
7627                if (trimmed.name) return make_node(AST_Assign, node, {
7628                    operator: node.operator,
7629                    left: trimmed.name,
7630                    right: trimmed.value,
7631                });
7632                if (trimmed.value) return trimmed.value;
7633                if (parent instanceof AST_Sequence && parent.tail_node() !== node) return List.skip;
7634                return make_node(AST_Number, node, { value: 0 });
7635            }
7636            if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
7637                // Certain combination of unused name + side effect leads to invalid AST:
7638                //    https://github.com/mishoo/UglifyJS/issues/1830
7639                // We fix it at this stage by moving the label inwards, back to the `for`.
7640                descend(node, tt);
7641                if (node.body instanceof AST_BlockStatement) {
7642                    var block = node.body;
7643                    node.body = block.body.pop();
7644                    block.body.push(node);
7645                    return in_list ? List.splice(block.body) : block;
7646                }
7647                return node;
7648            }
7649            if (node instanceof AST_Scope) {
7650                descend_scope();
7651                return node;
7652            }
7653            if (node instanceof AST_SymbolImport) {
7654                if (!compressor.option("imports") || node.definition().id in in_use_ids) return node;
7655                return in_list ? List.skip : null;
7656            }
7657
7658            function descend_scope() {
7659                var save_scope = scope;
7660                scope = node;
7661                descend(node, tt);
7662                scope = save_scope;
7663            }
7664        }, function(node, in_list) {
7665            if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
7666            if (node instanceof AST_ExportDeclaration) {
7667                var block = node.body;
7668                if (!(block instanceof AST_BlockStatement)) return;
7669                node.body = block.body.pop();
7670                block.body.push(node);
7671                return in_list ? List.splice(block.body) : block;
7672            }
7673            if (node instanceof AST_For) return patch_for_init(node, in_list);
7674            if (node instanceof AST_ForIn) {
7675                if (!drop_vars || !compressor.option("loops")) return;
7676                if (!is_empty(node.body)) return;
7677                var sym = get_init_symbol(node);
7678                if (!sym) return;
7679                var def = sym.definition();
7680                if (def.id in in_use_ids) return;
7681                log(sym, "Dropping unused loop variable {name}");
7682                if (for_ins[def.id] === node) delete for_ins[def.id];
7683                var body = [];
7684                var value = node.object.drop_side_effect_free(compressor);
7685                if (value) {
7686                    AST_Node.warn("Side effects in object of for-in loop [{start}]", value);
7687                    body.push(make_node(AST_SimpleStatement, node, { body: value }));
7688                }
7689                if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) {
7690                    body.push(node.init);
7691                }
7692                return insert_statements(body, node, in_list);
7693            }
7694            if (node instanceof AST_Import) {
7695                if (node.properties && node.properties.length == 0) node.properties = null;
7696                return node;
7697            }
7698            if (node instanceof AST_Sequence) {
7699                if (node.expressions.length > 1) return;
7700                return maintain_this_binding(tt.parent(), node, node.expressions[0]);
7701            }
7702        });
7703        tt.push(compressor.parent());
7704        tt.directives = Object.create(compressor.directives);
7705        self.transform(tt);
7706        if (self instanceof AST_Lambda
7707            && self.body.length == 1
7708            && self.body[0] instanceof AST_Directive
7709            && self.body[0].value == "use strict") {
7710            self.body.length = 0;
7711        }
7712        calls_to_drop_args.forEach(function(call) {
7713            drop_unused_call_args(call, compressor, fns_with_marked_args);
7714        });
7715
7716        function log(sym, text) {
7717            AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{start}]", sym);
7718        }
7719
7720        function log_default(node, text) {
7721            if (node.name instanceof AST_SymbolFunarg) {
7722                log(node.name, text);
7723            } else {
7724                AST_Node.info(text + " [{start}]", node);
7725            }
7726        }
7727
7728        function get_rvalue(expr) {
7729            return expr[expr instanceof AST_Assign ? "right" : "value"];
7730        }
7731
7732        function insert_statements(body, orig, in_list) {
7733            switch (body.length) {
7734              case 0:
7735                return in_list ? List.skip : make_node(AST_EmptyStatement, orig);
7736              case 1:
7737                return body[0];
7738              default:
7739                return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, { body: body });
7740            }
7741        }
7742
7743        function track_assigns(def, node) {
7744            if (def.scope.resolve() !== self) return false;
7745            if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
7746            return assign_in_use[def.id] !== false;
7747        }
7748
7749        function add_assigns(def, node) {
7750            if (!assign_in_use[def.id]) assign_in_use[def.id] = [];
7751            if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns);
7752        }
7753
7754        function indexOf_assign(def, node) {
7755            var nodes = assign_in_use[def.id];
7756            return nodes && nodes.indexOf(node);
7757        }
7758
7759        function unmark_lambda(def) {
7760            if (lambda_ids[def.id] > 1 && !(def.id in in_use_ids)) {
7761                in_use_ids[def.id] = true;
7762                in_use.push(def);
7763            }
7764            lambda_ids[def.id] = 0;
7765        }
7766
7767        function verify_safe_usage(def, read, modified) {
7768            if (def.id in in_use_ids) return;
7769            if (read && modified) {
7770                in_use_ids[def.id] = read;
7771                in_use.push(def);
7772            } else {
7773                value_read[def.id] = read;
7774                value_modified[def.id] = modified;
7775            }
7776        }
7777
7778        function can_drop_lhs(sym, node) {
7779            var def = sym.definition();
7780            var in_use = in_use_ids[def.id];
7781            if (!in_use) return true;
7782            if (node[node instanceof AST_Assign ? "left" : "expression"] !== sym) return false;
7783            return in_use === sym && def.references.length - def.replaced == 1 || indexOf_assign(def, node) < 0;
7784        }
7785
7786        function get_rhs(assign) {
7787            var rhs = assign.right;
7788            if (!assign.write_only) return rhs;
7789            if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
7790            if (!(rhs.left instanceof AST_SymbolRef)) return rhs;
7791            if (!(assign.left instanceof AST_SymbolRef)) return rhs;
7792            var def = assign.left.definition();
7793            if (rhs.left.definition() !== def) return rhs;
7794            if (rhs.right.has_side_effects(compressor)) return rhs;
7795            if (track_assigns(def, rhs.left)) add_assigns(def, rhs.left);
7796            return rhs.right;
7797        }
7798
7799        function get_init_symbol(for_in) {
7800            var init = for_in.init;
7801            if (init instanceof AST_Definitions) {
7802                init = init.definitions[0].name;
7803                return init instanceof AST_SymbolDeclaration && init;
7804            }
7805            while (init instanceof AST_PropAccess) init = init.expression.tail_node();
7806            if (init instanceof AST_SymbolRef) return init;
7807        }
7808
7809        function scan_ref_scoped(node, descend, init) {
7810            if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
7811                var def = node.left.definition();
7812                if (def.scope.resolve() === self) assignments.add(def.id, node);
7813            }
7814            if (node instanceof AST_SymbolRef && node.in_arg) var_defs[node.definition().id] = 0;
7815            if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
7816                var def = node.expression.definition();
7817                if (def.scope.resolve() === self) assignments.add(def.id, node);
7818            }
7819            var props = [], sym = assign_as_unused(node, props);
7820            if (sym) {
7821                var node_def = sym.definition();
7822                if (node_def.scope.resolve() !== self && self.variables.get(sym.name) !== node_def) return;
7823                if (is_arguments(node_def) && !all(self.argnames, function(argname) {
7824                    return !argname.match_symbol(function(node) {
7825                        if (node instanceof AST_SymbolFunarg) {
7826                            var def = node.definition();
7827                            return def.references.length > def.replaced;
7828                        }
7829                    }, true);
7830                })) return;
7831                if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return;
7832                var assign = props.assign;
7833                if (assign) {
7834                    assign.write_only = true;
7835                    assign.walk(tw);
7836                }
7837                props.forEach(function(prop) {
7838                    prop.walk(tw);
7839                });
7840                if (node instanceof AST_Assign) {
7841                    var right = get_rhs(node), shared = false;
7842                    if (init && node.write_only === true && !right.has_side_effects(compressor)) {
7843                        initializations.add(node_def.id, right);
7844                    } else {
7845                        right.walk(tw);
7846                        shared = right.tail_node().operator == "=";
7847                    }
7848                    if (node.left === sym) {
7849                        if (!node.write_only || shared) {
7850                            verify_safe_usage(node_def, sym, value_modified[node_def.id]);
7851                        }
7852                    } else {
7853                        var fixed = sym.fixed_value();
7854                        if (!fixed || !fixed.is_constant()) {
7855                            verify_safe_usage(node_def, value_read[node_def.id], true);
7856                        }
7857                    }
7858                }
7859                if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
7860                unmark_lambda(node_def);
7861                return true;
7862            }
7863            if (node instanceof AST_Binary) {
7864                if (node.operator != "instanceof") return;
7865                var sym = node.right;
7866                if (!(sym instanceof AST_SymbolRef)) return;
7867                var id = sym.definition().id;
7868                if (!lambda_ids[id]) return;
7869                node.left.walk(tw);
7870                lambda_ids[id]++;
7871                return true;
7872            }
7873            if (node instanceof AST_ForIn) {
7874                if (node.init instanceof AST_SymbolRef && scope === self) {
7875                    var id = node.init.definition().id;
7876                    if (!(id in for_ins)) for_ins[id] = node;
7877                }
7878                if (!drop_vars || !compressor.option("loops")) return;
7879                if (!is_empty(node.body)) return;
7880                if (node.init.has_side_effects(compressor)) return;
7881                var sym = get_init_symbol(node);
7882                if (!sym) return;
7883                var def = sym.definition();
7884                if (def.scope.resolve() !== self) {
7885                    var d = find_variable(sym.name);
7886                    if (d === def || d && d.redefined() === def) return;
7887                }
7888                node.object.walk(tw);
7889                return true;
7890            }
7891            if (node instanceof AST_SymbolRef) {
7892                var node_def = node.definition();
7893                if (!(node_def.id in in_use_ids)) {
7894                    in_use_ids[node_def.id] = true;
7895                    in_use.push(node_def);
7896                }
7897                if (cross_scope(node_def.scope, node.scope)) {
7898                    var redef = node_def.redefined();
7899                    if (redef && !(redef.id in in_use_ids)) {
7900                        in_use_ids[redef.id] = true;
7901                        in_use.push(redef);
7902                    }
7903                }
7904                if (track_assigns(node_def, node)) add_assigns(node_def, node);
7905                return true;
7906            }
7907            if (node instanceof AST_Scope) {
7908                var save_scope = scope;
7909                scope = node;
7910                descend();
7911                scope = save_scope;
7912                return true;
7913            }
7914        }
7915
7916        function is_decl(node) {
7917            return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration;
7918        }
7919
7920        function trim_decl(node) {
7921            if (node.definition().id in in_use_ids) return node;
7922            if (node instanceof AST_SymbolFunarg) node.unused = true;
7923            return null;
7924        }
7925
7926        function trim_default(trimmer, node) {
7927            node.value = node.value.transform(tt);
7928            var name = node.name.transform(trimmer);
7929            if (!name) {
7930                if (node.name instanceof AST_Destructured) return null;
7931                var value = node.value.drop_side_effect_free(compressor);
7932                if (!value) return null;
7933                log(node.name, "Side effects in default value of unused variable {name}");
7934                node = node.clone();
7935                node.name.unused = null;
7936                node.value = value;
7937            }
7938            return node;
7939        }
7940
7941        function trim_destructured(node, value, process, drop, root) {
7942            var trimmer = new TreeTransformer(function(node) {
7943                if (node instanceof AST_DefaultValue) {
7944                    if (!(compressor.option("default_values") && value && value.is_defined(compressor))) {
7945                        var save_drop = drop;
7946                        drop = false;
7947                        var trimmed = trim_default(trimmer, node);
7948                        drop = save_drop;
7949                        if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor);
7950                        return trimmed;
7951                    } else if (node === root) {
7952                        root = node = node.name;
7953                    } else {
7954                        node = node.name;
7955                    }
7956                }
7957                if (node instanceof AST_DestructuredArray) {
7958                    var save_drop = drop;
7959                    var save_value = value;
7960                    if (value instanceof AST_SymbolRef) {
7961                        drop = false;
7962                        value = value.fixed_value();
7963                    }
7964                    var native, values;
7965                    if (value instanceof AST_Array) {
7966                        native = true;
7967                        values = value.elements;
7968                    } else {
7969                        native = value && value.is_string(compressor);
7970                        values = false;
7971                    }
7972                    var elements = [], newValues = drop && [], pos = 0;
7973                    node.elements.forEach(function(element, index) {
7974                        value = values && values[index];
7975                        if (value instanceof AST_Hole) {
7976                            value = null;
7977                        } else if (value instanceof AST_Spread) {
7978                            if (drop) {
7979                                newValues.length = pos;
7980                                fill_holes(save_value, newValues);
7981                                [].push.apply(newValues, values.slice(index));
7982                                save_value.elements = newValues;
7983                            }
7984                            value = values = false;
7985                        }
7986                        element = element.transform(trimmer);
7987                        if (element) elements[pos] = element;
7988                        if (drop && value) newValues[pos] = value;
7989                        if (element || value || !drop || !values) pos++;
7990                    });
7991                    value = values && make_node(AST_Array, save_value, {
7992                        elements: values.slice(node.elements.length),
7993                    });
7994                    if (node.rest) {
7995                        var was_drop = drop;
7996                        drop = false;
7997                        node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7998                        drop = was_drop;
7999                        if (node.rest) elements.length = pos;
8000                    }
8001                    if (drop) {
8002                        if (value && !node.rest) value = value.drop_side_effect_free(compressor);
8003                        if (value instanceof AST_Array) {
8004                            value = value.elements;
8005                        } else if (value instanceof AST_Sequence) {
8006                            value = value.expressions;
8007                        } else if (value) {
8008                            value = [ value ];
8009                        }
8010                        if (value && value.length) {
8011                            newValues.length = pos;
8012                            [].push.apply(newValues, value);
8013                        }
8014                    }
8015                    value = save_value;
8016                    drop = save_drop;
8017                    if (values && newValues) {
8018                        fill_holes(value, newValues);
8019                        value = value.clone();
8020                        value.elements = newValues;
8021                    }
8022                    if (!native) {
8023                        elements.length = node.elements.length;
8024                    } else if (!node.rest) switch (elements.length) {
8025                      case 0:
8026                        if (node === root) break;
8027                        if (drop) value = value.drop_side_effect_free(compressor);
8028                        return null;
8029                      case 1:
8030                        if (!drop) break;
8031                        if (node === root) break;
8032                        var sym = elements[0];
8033                        if (sym.has_side_effects(compressor)) break;
8034                        if (value.has_side_effects(compressor) && sym.match_symbol(function(node) {
8035                            return node instanceof AST_PropAccess;
8036                        })) break;
8037                        value = make_node(AST_Sub, node, {
8038                            expression: value,
8039                            property: make_node(AST_Number, node, { value: 0 }),
8040                        });
8041                        return sym;
8042                    }
8043                    fill_holes(node, elements);
8044                    node.elements = elements;
8045                    return node;
8046                }
8047                if (node instanceof AST_DestructuredObject) {
8048                    var save_drop = drop;
8049                    var save_value = value;
8050                    if (value instanceof AST_SymbolRef) {
8051                        drop = false;
8052                        value = value.fixed_value();
8053                    }
8054                    var prop_keys, prop_map, values;
8055                    if (value instanceof AST_Object) {
8056                        prop_keys = [];
8057                        prop_map = new Dictionary();
8058                        values = value.properties.map(function(prop, index) {
8059                            prop = prop.clone();
8060                            if (prop instanceof AST_Spread) {
8061                                prop_map = false;
8062                            } else {
8063                                var key = prop.key;
8064                                if (key instanceof AST_Node) key = key.evaluate(compressor, true);
8065                                if (key instanceof AST_Node) {
8066                                    prop_map = false;
8067                                } else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
8068                                    prop_map.set(key, prop);
8069                                }
8070                                prop_keys[index] = key;
8071                            }
8072                            return prop;
8073                        });
8074                    }
8075                    if (node.rest) {
8076                        value = false;
8077                        node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
8078                    }
8079                    var can_drop = new Dictionary();
8080                    var drop_keys = drop && new Dictionary();
8081                    var properties = [];
8082                    node.properties.map(function(prop) {
8083                        var key = prop.key;
8084                        if (key instanceof AST_Node) {
8085                            prop.key = key = key.transform(tt);
8086                            key = key.evaluate(compressor, true);
8087                        }
8088                        if (key instanceof AST_Node) {
8089                            drop_keys = false;
8090                        } else {
8091                            can_drop.set(key, !can_drop.has(key));
8092                        }
8093                        return key;
8094                    }).forEach(function(key, index) {
8095                        var prop = node.properties[index], trimmed;
8096                        if (key instanceof AST_Node) {
8097                            drop = false;
8098                            value = false;
8099                            trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
8100                        } else {
8101                            drop = drop_keys && can_drop.get(key);
8102                            var mapped = prop_map && prop_map.get(key);
8103                            if (mapped) {
8104                                value = mapped.value;
8105                                if (value instanceof AST_Accessor) value = false;
8106                            } else {
8107                                value = false;
8108                            }
8109                            trimmed = prop.value.transform(trimmer);
8110                            if (!trimmed) {
8111                                if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
8112                                if (drop_keys && !drop_keys.has(key)) {
8113                                    if (mapped) {
8114                                        drop_keys.set(key, mapped);
8115                                        if (value === null) {
8116                                            prop_map.set(key, retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
8117                                                key: mapped.key,
8118                                                value: make_node(AST_Number, mapped, { value: 0 }),
8119                                            }));
8120                                        }
8121                                    } else {
8122                                        drop_keys.set(key, true);
8123                                    }
8124                                }
8125                            } else if (drop_keys) {
8126                                drop_keys.set(key, false);
8127                            }
8128                            if (value) mapped.value = value;
8129                        }
8130                        if (trimmed) {
8131                            prop.value = trimmed;
8132                            properties.push(prop);
8133                        }
8134                    });
8135                    value = save_value;
8136                    drop = save_drop;
8137                    if (drop_keys && prop_keys) {
8138                        value = value.clone();
8139                        value.properties = List(values, function(prop, index) {
8140                            if (prop instanceof AST_Spread) return prop;
8141                            var key = prop_keys[index];
8142                            if (key instanceof AST_Node) return prop;
8143                            if (drop_keys.has(key)) {
8144                                var mapped = drop_keys.get(key);
8145                                if (!mapped) return prop;
8146                                if (mapped === prop) return prop_map.get(key) || List.skip;
8147                            } else if (node.rest) {
8148                                return prop;
8149                            }
8150                            var trimmed = prop.value.drop_side_effect_free(compressor);
8151                            if (trimmed) {
8152                                prop.value = trimmed;
8153                                return prop;
8154                            }
8155                            return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, {
8156                                key: prop.key,
8157                                value: make_node(AST_Number, prop, { value: 0 }),
8158                            }) : List.skip;
8159                        });
8160                    }
8161                    if (value && !node.rest) switch (properties.length) {
8162                      case 0:
8163                        if (node === root) break;
8164                        if (value.may_throw_on_access(compressor, true)) break;
8165                        if (drop) value = value.drop_side_effect_free(compressor);
8166                        return null;
8167                      case 1:
8168                        if (!drop) break;
8169                        if (node === root) break;
8170                        var prop = properties[0];
8171                        if (prop.key instanceof AST_Node) break;
8172                        if (prop.value.has_side_effects(compressor)) break;
8173                        if (value.has_side_effects(compressor) && prop.value.match_symbol(function(node) {
8174                            return node instanceof AST_PropAccess;
8175                        })) break;
8176                        value = make_node(AST_Sub, node, {
8177                            expression: value,
8178                            property: make_node_from_constant(prop.key, prop),
8179                        });
8180                        return prop.value;
8181                    }
8182                    node.properties = properties;
8183                    return node;
8184                }
8185                if (node instanceof AST_Hole) {
8186                    node = null;
8187                } else {
8188                    node = process(node);
8189                }
8190                if (!node && drop && value) value = value.drop_side_effect_free(compressor);
8191                return node;
8192            });
8193            return {
8194                name: node.transform(trimmer),
8195                value: value,
8196            };
8197
8198            function retain_key(prop) {
8199                return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor);
8200            }
8201
8202            function clear_write_only(node) {
8203                if (node instanceof AST_Assign) {
8204                    node.write_only = false;
8205                    clear_write_only(node.right);
8206                } else if (node instanceof AST_Binary) {
8207                    if (!lazy_op[node.operator]) return;
8208                    clear_write_only(node.left);
8209                    clear_write_only(node.right);
8210                } else if (node instanceof AST_Conditional) {
8211                    clear_write_only(node.consequent);
8212                    clear_write_only(node.alternative);
8213                } else if (node instanceof AST_Sequence) {
8214                    clear_write_only(node.tail_node());
8215                } else if (node instanceof AST_Unary) {
8216                    node.write_only = false;
8217                }
8218            }
8219
8220            function retain_lhs(node) {
8221                if (node instanceof AST_DefaultValue) return retain_lhs(node.name);
8222                if (node instanceof AST_Destructured) {
8223                    if (value === null) {
8224                        value = make_node(AST_Number, node, { value: 0 });
8225                    } else if (value) {
8226                        if (value.may_throw_on_access(compressor, true)) {
8227                            value = make_node(AST_Array, node, {
8228                                elements: value instanceof AST_Sequence ? value.expressions : [ value ],
8229                            });
8230                        } else {
8231                            clear_write_only(value);
8232                        }
8233                    }
8234                    return make_node(AST_DestructuredObject, node, { properties: [] });
8235                }
8236                node.unused = null;
8237                return node;
8238            }
8239        }
8240    });
8241
8242    AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
8243        if (compressor.has_directive("use asm")) return;
8244        var hoist_funs = compressor.option("hoist_funs");
8245        var hoist_vars = compressor.option("hoist_vars");
8246        var self = this;
8247        if (hoist_vars) {
8248            // let's count var_decl first, we seem to waste a lot of
8249            // space if we hoist `var` when there's only one.
8250            var var_decl = 0;
8251            self.walk(new TreeWalker(function(node) {
8252                if (var_decl > 1) return true;
8253                if (node instanceof AST_ExportDeclaration) return true;
8254                if (node instanceof AST_Scope && node !== self) return true;
8255                if (node instanceof AST_Var) {
8256                    var_decl++;
8257                    return true;
8258                }
8259            }));
8260            if (var_decl <= 1) hoist_vars = false;
8261        }
8262        if (!hoist_funs && !hoist_vars) return;
8263        var consts = new Dictionary();
8264        var dirs = [];
8265        var hoisted = [];
8266        var vars = new Dictionary();
8267        var tt = new TreeTransformer(function(node, descend, in_list) {
8268            if (node === self) return;
8269            if (node instanceof AST_Directive) {
8270                dirs.push(node);
8271                return in_list ? List.skip : make_node(AST_EmptyStatement, node);
8272            }
8273            if (node instanceof AST_LambdaDefinition) {
8274                if (!hoist_funs) return node;
8275                var p = tt.parent();
8276                if (p instanceof AST_ExportDeclaration) return node;
8277                if (p instanceof AST_ExportDefault) return node;
8278                if (p !== self && compressor.has_directive("use strict")) return node;
8279                hoisted.push(node);
8280                return in_list ? List.skip : make_node(AST_EmptyStatement, node);
8281            }
8282            if (node instanceof AST_Var) {
8283                if (!hoist_vars) return node;
8284                var p = tt.parent();
8285                if (p instanceof AST_ExportDeclaration) return node;
8286                if (!all(node.definitions, function(defn) {
8287                    var sym = defn.name;
8288                    return sym instanceof AST_SymbolVar
8289                        && !consts.has(sym.name)
8290                        && self.find_variable(sym.name) === sym.definition();
8291                })) return node;
8292                node.definitions.forEach(function(defn) {
8293                    vars.set(defn.name.name, defn);
8294                });
8295                var seq = node.to_assignments();
8296                if (p instanceof AST_ForEnumeration && p.init === node) {
8297                    if (seq) return seq;
8298                    var sym = node.definitions[0].name;
8299                    return make_node(AST_SymbolRef, sym);
8300                }
8301                if (p instanceof AST_For && p.init === node) return seq;
8302                if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
8303                return make_node(AST_SimpleStatement, node, { body: seq });
8304            }
8305            if (node instanceof AST_Scope) return node;
8306            if (node instanceof AST_SymbolConst) {
8307                consts.set(node.name, true);
8308                return node;
8309            }
8310        });
8311        self.transform(tt);
8312        if (vars.size() > 0) {
8313            // collect only vars which don't show up in self's arguments list
8314            var defns = [];
8315            if (self instanceof AST_Lambda) self.each_argname(function(argname) {
8316                if (all(argname.definition().references, function(ref) {
8317                    return !ref.in_arg;
8318                })) vars.del(argname.name);
8319            });
8320            vars.each(function(defn, name) {
8321                defn = defn.clone();
8322                defn.name = defn.name.clone();
8323                defn.value = null;
8324                defns.push(defn);
8325                vars.set(name, defn);
8326                defn.name.definition().orig.unshift(defn.name);
8327            });
8328            if (defns.length > 0) hoisted.push(make_node(AST_Var, self, { definitions: defns }));
8329        }
8330        self.body = dirs.concat(hoisted, self.body);
8331    });
8332
8333    function scan_local_returns(fn, transform) {
8334        fn.walk(new TreeWalker(function(node) {
8335            if (node instanceof AST_Return) {
8336                transform(node);
8337                return true;
8338            }
8339            if (node instanceof AST_Scope && node !== fn) return true;
8340        }));
8341    }
8342
8343    function map_self_returns(fn) {
8344        var map = Object.create(null);
8345        scan_local_returns(fn, function(node) {
8346            var value = node.value;
8347            if (value) value = value.tail_node();
8348            if (value instanceof AST_SymbolRef) {
8349                var id = value.definition().id;
8350                map[id] = (map[id] || 0) + 1;
8351            }
8352        });
8353        return map;
8354    }
8355
8356    function can_trim_returns(def, self_returns, compressor) {
8357        if (compressor.exposed(def)) return false;
8358        switch (def.references.length - def.replaced - (self_returns[def.id] || 0)) {
8359          case def.drop_return:
8360            return "d";
8361          case def.bool_return:
8362            return true;
8363        }
8364    }
8365
8366    function process_boolean_returns(fn, compressor) {
8367        scan_local_returns(fn, function(node) {
8368            node.in_bool = true;
8369            var value = node.value;
8370            if (value) {
8371                var ev = fuzzy_eval(compressor, value);
8372                if (!ev) {
8373                    value = value.drop_side_effect_free(compressor);
8374                    node.value = value ? make_sequence(node.value, [
8375                        value,
8376                        make_node(AST_Number, node.value, { value: 0 }),
8377                    ]) : null;
8378                } else if (!(ev instanceof AST_Node)) {
8379                    value = value.drop_side_effect_free(compressor);
8380                    node.value = value ? make_sequence(node.value, [
8381                        value,
8382                        make_node(AST_Number, node.value, { value: 1 }),
8383                    ]) : make_node(AST_Number, node.value, { value: 1 });
8384                }
8385            }
8386        });
8387    }
8388
8389    AST_Scope.DEFMETHOD("process_returns", noop);
8390    AST_Defun.DEFMETHOD("process_returns", function(compressor) {
8391        if (!compressor.option("booleans")) return;
8392        if (compressor.parent() instanceof AST_ExportDefault) return;
8393        switch (can_trim_returns(this.name.definition(), map_self_returns(this), compressor)) {
8394          case "d":
8395            drop_returns(compressor, this, true);
8396            break;
8397          case true:
8398            process_boolean_returns(this, compressor);
8399            break;
8400        }
8401    });
8402    AST_Function.DEFMETHOD("process_returns", function(compressor) {
8403        if (!compressor.option("booleans")) return;
8404        var drop = true;
8405        var self_returns = map_self_returns(this);
8406        if (this.name && !can_trim(this.name.definition())) return;
8407        var parent = compressor.parent();
8408        if (parent instanceof AST_Assign) {
8409            if (parent.operator != "=") return;
8410            var sym = parent.left;
8411            if (!(sym instanceof AST_SymbolRef)) return;
8412            if (!can_trim(sym.definition())) return;
8413        } else if (parent instanceof AST_Call && parent.expression !== this) {
8414            var exp = parent.expression;
8415            if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
8416            if (!(exp instanceof AST_Lambda)) return;
8417            if (exp.uses_arguments || exp.pinned()) return;
8418            var args = parent.args, sym;
8419            for (var i = 0; i < args.length; i++) {
8420                var arg = args[i];
8421                if (arg === this) {
8422                    sym = exp.argnames[i];
8423                    if (!sym && exp.rest) return;
8424                    break;
8425                }
8426                if (arg instanceof AST_Spread) return;
8427            }
8428            if (sym instanceof AST_DefaultValue) sym = sym.name;
8429            if (sym instanceof AST_SymbolFunarg && !can_trim(sym.definition())) return;
8430        } else if (parent.TYPE == "Call") {
8431            compressor.pop();
8432            var in_bool = compressor.in_boolean_context();
8433            compressor.push(this);
8434            switch (in_bool) {
8435              case true:
8436                drop = false;
8437              case "d":
8438                break;
8439              default:
8440                return;
8441            }
8442        } else return;
8443        if (drop) {
8444            drop_returns(compressor, this, true);
8445        } else {
8446            process_boolean_returns(this, compressor);
8447        }
8448
8449        function can_trim(def) {
8450            switch (can_trim_returns(def, self_returns, compressor)) {
8451              case true:
8452                drop = false;
8453              case "d":
8454                return true;
8455            }
8456        }
8457    });
8458
8459    AST_BlockScope.DEFMETHOD("var_names", function() {
8460        var var_names = this._var_names;
8461        if (!var_names) {
8462            this._var_names = var_names = new Dictionary();
8463            this.enclosed.forEach(function(def) {
8464                var_names.set(def.name, true);
8465            });
8466            this.variables.each(function(def, name) {
8467                var_names.set(name, true);
8468            });
8469        }
8470        return var_names;
8471    });
8472
8473    AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
8474        var scopes = [ this ];
8475        if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
8476            var s = ref.scope;
8477            do {
8478                if (!push_uniq(scopes, s)) return;
8479                s = s.parent_scope;
8480            } while (s && s !== this);
8481        });
8482        prefix = prefix.replace(/^[^a-z_$]|[^a-z0-9_$]/gi, "_");
8483        var name = prefix;
8484        for (var i = 0; !all(scopes, function(scope) {
8485            return !scope.var_names().has(name);
8486        }); i++) name = prefix + "$" + i;
8487        var sym = make_node(type, orig, {
8488            name: name,
8489            scope: this,
8490        });
8491        var def = this.def_variable(sym);
8492        scopes.forEach(function(scope) {
8493            scope.enclosed.push(def);
8494            scope.var_names().set(name, true);
8495        });
8496        return sym;
8497    });
8498
8499    AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
8500        if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
8501        var self = this;
8502        if (is_arrow(self) && self.value) return;
8503        var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
8504        var defs_by_id = Object.create(null);
8505        var tt = new TreeTransformer(function(node, descend) {
8506            if (node instanceof AST_Assign) {
8507                if (node.operator != "=") return;
8508                if (!node.write_only) return;
8509                if (!can_hoist(node.left, node.right, 1)) return;
8510                descend(node, tt);
8511                var defs = new Dictionary();
8512                var assignments = [];
8513                var decls = [];
8514                node.right.properties.forEach(function(prop) {
8515                    var decl = make_sym(AST_SymbolVar, node.left, prop.key);
8516                    decls.push(make_node(AST_VarDef, node, {
8517                        name: decl,
8518                        value: null,
8519                    }));
8520                    var sym = make_node(AST_SymbolRef, node, {
8521                        name: decl.name,
8522                        scope: self,
8523                        thedef: decl.definition(),
8524                    });
8525                    sym.reference();
8526                    assignments.push(make_node(AST_Assign, node, {
8527                        operator: "=",
8528                        left: sym,
8529                        right: prop.value,
8530                    }));
8531                });
8532                defs.value = node.right;
8533                defs_by_id[node.left.definition().id] = defs;
8534                self.body.splice(self.body.indexOf(tt.stack[1]) + 1, 0, make_node(AST_Var, node, {
8535                    definitions: decls,
8536                }));
8537                return make_sequence(node, assignments);
8538            }
8539            if (node instanceof AST_Scope) {
8540                if (node === self) return;
8541                var parent = tt.parent();
8542                if (parent.TYPE == "Call" && parent.expression === node) return;
8543                return node;
8544            }
8545            if (node instanceof AST_VarDef) {
8546                if (!can_hoist(node.name, node.value, 0)) return;
8547                descend(node, tt);
8548                var defs = new Dictionary();
8549                var var_defs = [];
8550                var decl = node.clone();
8551                decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null;
8552                var_defs.push(decl);
8553                node.value.properties.forEach(function(prop) {
8554                    var_defs.push(make_node(AST_VarDef, node, {
8555                        name: make_sym(node.name.CTOR, node.name, prop.key),
8556                        value: prop.value,
8557                    }));
8558                });
8559                defs.value = node.value;
8560                defs_by_id[node.name.definition().id] = defs;
8561                return List.splice(var_defs);
8562            }
8563
8564            function make_sym(type, sym, key) {
8565                var new_var = self.make_var(type, sym, sym.name + "_" + key);
8566                defs.set(key, new_var.definition());
8567                return new_var;
8568            }
8569        });
8570        self.transform(tt);
8571        self.transform(new TreeTransformer(function(node, descend) {
8572            if (node instanceof AST_PropAccess) {
8573                if (!(node.expression instanceof AST_SymbolRef)) return;
8574                var defs = defs_by_id[node.expression.definition().id];
8575                if (!defs) return;
8576                if (node.expression.fixed_value() !== defs.value) return;
8577                var def = defs.get(node.get_property());
8578                var sym = make_node(AST_SymbolRef, node, {
8579                    name: def.name,
8580                    scope: node.expression.scope,
8581                    thedef: def,
8582                });
8583                sym.reference();
8584                return sym;
8585            }
8586            if (node instanceof AST_SymbolRef) {
8587                var defs = defs_by_id[node.definition().id];
8588                if (!defs) return;
8589                if (node.fixed_value() !== defs.value) return;
8590                return make_node(AST_Object, node, { properties: [] });
8591            }
8592        }));
8593
8594        function can_hoist(sym, right, count) {
8595            if (!(sym instanceof AST_Symbol)) return;
8596            var def = sym.definition();
8597            if (def.assignments != count) return;
8598            if (def.references.length - def.replaced == count) return;
8599            if (def.single_use) return;
8600            if (self.find_variable(sym.name) !== def) return;
8601            if (top_retain(def)) return;
8602            if (sym.fixed_value() !== right) return;
8603            var fixed = sym.fixed || def.fixed;
8604            if (fixed.direct_access) return;
8605            if (fixed.escaped && fixed.escaped.depth == 1) return;
8606            return right instanceof AST_Object
8607                && right.properties.length > 0
8608                && can_drop_symbol(sym, compressor)
8609                && all(right.properties, function(prop) {
8610                    return can_hoist_property(prop) && prop.key !== "__proto__";
8611                });
8612        }
8613    });
8614
8615    function fn_name_unused(fn, compressor) {
8616        if (!fn.name || !compressor.option("ie")) return true;
8617        var def = fn.name.definition();
8618        if (compressor.exposed(def)) return false;
8619        return all(def.references, function(sym) {
8620            return !(sym instanceof AST_SymbolRef);
8621        });
8622    }
8623
8624    function drop_returns(compressor, exp, ignore_name) {
8625        if (!(exp instanceof AST_Lambda)) return;
8626        var arrow = is_arrow(exp);
8627        var async = is_async(exp);
8628        var changed = false;
8629        var drop_body = false;
8630        if (arrow && compressor.option("arrows")) {
8631            if (!exp.value) {
8632                drop_body = true;
8633            } else if (!async || needs_enqueuing(compressor, exp.value)) {
8634                var dropped = exp.value.drop_side_effect_free(compressor);
8635                if (dropped !== exp.value) {
8636                    changed = true;
8637                    exp.value = dropped;
8638                }
8639            }
8640        } else if (!is_generator(exp)) {
8641            if (!ignore_name && exp.name) {
8642                var def = exp.name.definition();
8643                drop_body = def.references.length == def.replaced;
8644            } else {
8645                drop_body = true;
8646            }
8647        }
8648        if (drop_body) {
8649            exp.process_expression(false, function(node) {
8650                var value = node.value;
8651                if (value) {
8652                    if (async && !needs_enqueuing(compressor, value)) return node;
8653                    value = value.drop_side_effect_free(compressor, true);
8654                }
8655                changed = true;
8656                if (!value) return make_node(AST_EmptyStatement, node);
8657                return make_node(AST_SimpleStatement, node, { body: value });
8658            });
8659            scan_local_returns(exp, function(node) {
8660                var value = node.value;
8661                if (value) {
8662                    if (async && !needs_enqueuing(compressor, value)) return;
8663                    var dropped = value.drop_side_effect_free(compressor);
8664                    if (dropped !== value) {
8665                        changed = true;
8666                        if (dropped && async && !needs_enqueuing(compressor, dropped)) {
8667                            dropped = dropped.negate(compressor);
8668                        }
8669                        node.value = dropped;
8670                    }
8671                }
8672            });
8673        }
8674        if (async && compressor.option("awaits")) {
8675            if (drop_body) exp.process_expression("awaits", function(node) {
8676                var body = node.body;
8677                if (body instanceof AST_Await) {
8678                    if (needs_enqueuing(compressor, body.expression)) {
8679                        changed = true;
8680                        body = body.expression.drop_side_effect_free(compressor, true);
8681                        if (!body) return make_node(AST_EmptyStatement, node);
8682                        node.body = body;
8683                    }
8684                } else if (body instanceof AST_Sequence) {
8685                    var exprs = body.expressions;
8686                    for (var i = exprs.length; --i >= 0;) {
8687                        var tail = exprs[i];
8688                        if (!(tail instanceof AST_Await)) break;
8689                        var value = tail.expression;
8690                        if (!needs_enqueuing(compressor, value)) break;
8691                        changed = true;
8692                        if (exprs[i] = value.drop_side_effect_free(compressor)) break;
8693                    }
8694                    switch (i) {
8695                      case -1:
8696                        return make_node(AST_EmptyStatement, node);
8697                      case 0:
8698                        node.body = exprs[0];
8699                        break;
8700                      default:
8701                        exprs.length = i + 1;
8702                        break;
8703                    }
8704                }
8705                return node;
8706            });
8707            var abort = !drop_body && exp.name || arrow && exp.value && !needs_enqueuing(compressor, exp.value);
8708            var tw = new TreeWalker(function(node) {
8709                if (abort) return true;
8710                if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
8711                if (node instanceof AST_Await) return abort = true;
8712                if (node instanceof AST_ForAwaitOf) return abort = true;
8713                if (node instanceof AST_Return) {
8714                    if (node.value && !needs_enqueuing(compressor, node.value)) return abort = true;
8715                    return;
8716                }
8717                if (node instanceof AST_Scope && node !== exp) return true;
8718            });
8719            exp.walk(tw);
8720            if (!abort) {
8721                var ctor;
8722                switch (exp.CTOR) {
8723                  case AST_AsyncArrow:
8724                    ctor = AST_Arrow;
8725                    break;
8726                  case AST_AsyncFunction:
8727                    ctor = AST_Function;
8728                    break;
8729                  case AST_AsyncGeneratorFunction:
8730                    ctor = AST_GeneratorFunction;
8731                    break;
8732                }
8733                return make_node(ctor, exp);
8734            }
8735        }
8736        return changed && exp.clone();
8737    }
8738
8739    // drop_side_effect_free()
8740    // remove side-effect-free parts which only affects return value
8741    (function(def) {
8742        // Drop side-effect-free elements from an array of expressions.
8743        // Returns an array of expressions with side-effects or null
8744        // if all elements were dropped. Note: original array may be
8745        // returned if nothing changed.
8746        function trim(nodes, compressor, first_in_statement, spread) {
8747            var len = nodes.length;
8748            var ret = [], changed = false;
8749            for (var i = 0; i < len; i++) {
8750                var node = nodes[i];
8751                var trimmed;
8752                if (spread && node instanceof AST_Spread) {
8753                    trimmed = spread(node, compressor, first_in_statement);
8754                } else {
8755                    trimmed = node.drop_side_effect_free(compressor, first_in_statement);
8756                }
8757                if (trimmed !== node) changed = true;
8758                if (trimmed) {
8759                    ret.push(trimmed);
8760                    first_in_statement = false;
8761                }
8762            }
8763            return ret.length ? changed ? ret : nodes : null;
8764        }
8765        function array_spread(node, compressor, first_in_statement) {
8766            var exp = node.expression;
8767            if (!exp.is_string(compressor)) return node;
8768            return exp.drop_side_effect_free(compressor, first_in_statement);
8769        }
8770        function convert_spread(node) {
8771            return node instanceof AST_Spread ? make_node(AST_Array, node, { elements: [ node ] }) : node;
8772        }
8773        def(AST_Node, return_this);
8774        def(AST_Accessor, return_null);
8775        def(AST_Array, function(compressor, first_in_statement) {
8776            var values = trim(this.elements, compressor, first_in_statement, array_spread);
8777            if (!values) return null;
8778            if (values === this.elements && all(values, function(node) {
8779                return node instanceof AST_Spread;
8780            })) return this;
8781            return make_sequence(this, values.map(convert_spread));
8782        });
8783        def(AST_Assign, function(compressor) {
8784            var left = this.left;
8785            if (left instanceof AST_PropAccess) {
8786                var expr = left.expression;
8787                if (expr.may_throw_on_access(compressor, true)) return this;
8788                if (compressor.has_directive("use strict") && expr.is_constant()) return this;
8789            }
8790            if (left.has_side_effects(compressor)) return this;
8791            if (lazy_op[this.operator.slice(0, -1)]) return this;
8792            this.write_only = true;
8793            if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this;
8794            return this.right.drop_side_effect_free(compressor);
8795        });
8796        def(AST_Await, function(compressor) {
8797            if (!compressor.option("awaits")) return this;
8798            var exp = this.expression;
8799            if (!needs_enqueuing(compressor, exp)) return this;
8800            if (exp instanceof AST_UnaryPrefix && exp.operator == "!") exp = exp.expression;
8801            var dropped = exp.drop_side_effect_free(compressor);
8802            if (dropped === exp) return this;
8803            if (!dropped) {
8804                dropped = make_node(AST_Number, exp, { value: 0 });
8805            } else if (!needs_enqueuing(compressor, dropped)) {
8806                dropped = dropped.negate(compressor);
8807            }
8808            var node = this.clone();
8809            node.expression = dropped;
8810            return node;
8811        });
8812        def(AST_Binary, function(compressor, first_in_statement) {
8813            var left = this.left;
8814            var right = this.right;
8815            var op = this.operator;
8816            if (!can_drop_op(op, right, compressor)) {
8817                var lhs = left.drop_side_effect_free(compressor, first_in_statement);
8818                if (lhs === left) return this;
8819                var node = this.clone();
8820                node.left = lhs || make_node(AST_Number, left, { value: 0 });
8821                return node;
8822            }
8823            var rhs = right.drop_side_effect_free(compressor, first_in_statement);
8824            if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement);
8825            if (lazy_op[op] && rhs.has_side_effects(compressor)) {
8826                var node = this;
8827                if (rhs !== right) {
8828                    node = node.clone();
8829                    node.right = rhs.drop_side_effect_free(compressor);
8830                }
8831                if (op == "??") return node;
8832                var negated = node.clone();
8833                negated.operator = op == "&&" ? "||" : "&&";
8834                negated.left = left.negate(compressor, first_in_statement);
8835                var negated_rhs = negated.right.tail_node();
8836                if (negated_rhs instanceof AST_Binary && negated.operator == negated_rhs.operator) swap_chain(negated);
8837                var best = first_in_statement ? best_of_statement : best_of_expression;
8838                return op == "&&" ? best(node, negated) : best(negated, node);
8839            }
8840            var lhs = left.drop_side_effect_free(compressor, first_in_statement);
8841            if (!lhs) return rhs;
8842            rhs = rhs.drop_side_effect_free(compressor);
8843            if (!rhs) return lhs;
8844            return make_sequence(this, [ lhs, rhs ]);
8845        });
8846        function assign_this_only(fn, compressor) {
8847            fn.new = true;
8848            var result = all(fn.body, function(stat) {
8849                return !stat.has_side_effects(compressor);
8850            }) && all(fn.argnames, function(argname) {
8851                return !argname.match_symbol(return_false);
8852            }) && !(fn.rest && fn.rest.match_symbol(return_false));
8853            fn.new = false;
8854            return result;
8855        }
8856        def(AST_Call, function(compressor, first_in_statement) {
8857            var self = this;
8858            if (self.is_expr_pure(compressor)) {
8859                if (self.pure) AST_Node.warn("Dropping __PURE__ call [{start}]", self);
8860                var args = trim(self.args, compressor, first_in_statement, array_spread);
8861                return args && make_sequence(self, args.map(convert_spread));
8862            }
8863            var exp = self.expression;
8864            if (self.is_call_pure(compressor)) {
8865                var exprs = self.args.slice();
8866                exprs.unshift(exp.expression);
8867                exprs = trim(exprs, compressor, first_in_statement, array_spread);
8868                return exprs && make_sequence(self, exprs.map(convert_spread));
8869            }
8870            if (compressor.option("yields") && is_generator(exp)) {
8871                var call = self.clone();
8872                call.expression = make_node(AST_Function, exp);
8873                call.expression.body = [];
8874                var opt = call.transform(compressor);
8875                if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
8876            }
8877            var dropped = drop_returns(compressor, exp);
8878            if (dropped) {
8879                // always shallow clone to ensure stripping of negated IIFEs
8880                self = self.clone();
8881                self.expression = dropped;
8882                // avoid extraneous traversal
8883                if (exp._squeezed) self.expression._squeezed = true;
8884            }
8885            if (self instanceof AST_New) {
8886                var fn = exp;
8887                if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
8888                if (fn instanceof AST_Lambda) {
8889                    if (assign_this_only(fn, compressor)) {
8890                        var exprs = self.args.slice();
8891                        exprs.unshift(exp);
8892                        exprs = trim(exprs, compressor, first_in_statement, array_spread);
8893                        return exprs && make_sequence(self, exprs.map(convert_spread));
8894                    }
8895                    if (!fn.contains_this()) {
8896                        self = make_node(AST_Call, self);
8897                        self.expression = self.expression.clone();
8898                        self.args = self.args.slice();
8899                    }
8900                }
8901            }
8902            self.call_only = true;
8903            return self;
8904        });
8905        def(AST_ClassExpression, function(compressor, first_in_statement) {
8906            var self = this;
8907            var exprs = [], values = [], init = 0;
8908            var props = self.properties;
8909            for (var i = 0; i < props.length; i++) {
8910                var prop = props[i];
8911                if (prop.key instanceof AST_Node) exprs.push(prop.key);
8912                if (!is_static_field_or_init(prop)) continue;
8913                var value = prop.value;
8914                if (!value.has_side_effects(compressor)) continue;
8915                if (value.contains_this()) return self;
8916                if (prop instanceof AST_ClassInit) {
8917                    init++;
8918                    values.push(prop);
8919                } else {
8920                    values.push(value);
8921                }
8922            }
8923            var base = self.extends;
8924            if (base) {
8925                if (base instanceof AST_SymbolRef) base = base.fixed_value();
8926                base = !safe_for_extends(base);
8927                if (!base) exprs.unshift(self.extends);
8928            }
8929            exprs = trim(exprs, compressor, first_in_statement);
8930            if (exprs) first_in_statement = false;
8931            values = trim(values, compressor, first_in_statement);
8932            if (!exprs) {
8933                if (!base && !values && !self.name) return null;
8934                exprs = [];
8935            }
8936            if (base || self.name || !compressor.has_directive("use strict")) {
8937                var node = to_class_expr(self);
8938                if (!base) node.extends = null;
8939                node.properties = [];
8940                if (values) {
8941                    if (values.length == init) {
8942                        if (exprs.length) values.unshift(make_node(AST_ClassField, self, {
8943                            key: make_sequence(self, exprs),
8944                            value: null,
8945                        }));
8946                        node.properties = values;
8947                    } else node.properties.push(make_node(AST_ClassField, self, {
8948                        static: true,
8949                        key: exprs.length ? make_sequence(self, exprs) : "c",
8950                        value: make_value(),
8951                    }));
8952                } else if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, {
8953                    key: make_sequence(self, exprs),
8954                    value: make_node(AST_Function, self, {
8955                        argnames: [],
8956                        body: [],
8957                    }).init_vars(node),
8958                }));
8959                return node;
8960            }
8961            if (values) exprs.push(make_node(AST_Call, self, {
8962                expression: make_node(AST_Arrow, self, {
8963                    argnames: [],
8964                    body: [],
8965                    value: make_value(),
8966                }).init_vars(self.parent_scope),
8967                args: [],
8968            }));
8969            return make_sequence(self, exprs);
8970
8971            function make_value() {
8972                return make_sequence(self, values.map(function(node) {
8973                    if (!(node instanceof AST_ClassInit)) return node;
8974                    var fn = make_node(AST_Arrow, node.value);
8975                    fn.argnames = [];
8976                    return make_node(AST_Call, node, {
8977                        expression: fn,
8978                        args: [],
8979                    });
8980                }));
8981            }
8982        });
8983        def(AST_Conditional, function(compressor) {
8984            var consequent = this.consequent.drop_side_effect_free(compressor);
8985            var alternative = this.alternative.drop_side_effect_free(compressor);
8986            if (consequent === this.consequent && alternative === this.alternative) return this;
8987            var exprs;
8988            if (compressor.option("ie")) {
8989                exprs = [];
8990                if (consequent instanceof AST_Function) {
8991                    exprs.push(consequent);
8992                    consequent = null;
8993                }
8994                if (alternative instanceof AST_Function) {
8995                    exprs.push(alternative);
8996                    alternative = null;
8997                }
8998            }
8999            var node;
9000            if (!consequent) {
9001                node = alternative ? make_node(AST_Binary, this, {
9002                    operator: "||",
9003                    left: this.condition,
9004                    right: alternative,
9005                }) : this.condition.drop_side_effect_free(compressor);
9006            } else if (!alternative) {
9007                node = make_node(AST_Binary, this, {
9008                    operator: "&&",
9009                    left: this.condition,
9010                    right: consequent,
9011                });
9012            } else {
9013                node = this.clone();
9014                node.consequent = consequent;
9015                node.alternative = alternative;
9016            }
9017            if (!exprs) return node;
9018            if (node) exprs.push(node);
9019            return exprs.length == 0 ? null : make_sequence(this, exprs);
9020        });
9021        def(AST_Constant, return_null);
9022        def(AST_Dot, function(compressor, first_in_statement) {
9023            var expr = this.expression;
9024            if (expr.may_throw_on_access(compressor)) return this;
9025            return expr.drop_side_effect_free(compressor, first_in_statement);
9026        });
9027        def(AST_Function, function(compressor) {
9028            return fn_name_unused(this, compressor) ? null : this;
9029        });
9030        def(AST_LambdaExpression, return_null);
9031        def(AST_Object, function(compressor, first_in_statement) {
9032            var exprs = [];
9033            this.properties.forEach(function(prop) {
9034                if (prop instanceof AST_Spread) {
9035                    exprs.push(prop);
9036                } else {
9037                    if (prop.key instanceof AST_Node) exprs.push(prop.key);
9038                    exprs.push(prop.value);
9039                }
9040            });
9041            var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
9042                var exp = node.expression;
9043                return exp.safe_to_spread() ? exp.drop_side_effect_free(compressor, first_in_statement) : node;
9044            });
9045            if (!values) return null;
9046            if (values === exprs && !all(values, function(node) {
9047                return !(node instanceof AST_Spread);
9048            })) return this;
9049            return make_sequence(this, values.map(function(node) {
9050                return node instanceof AST_Spread ? make_node(AST_Object, node, { properties: [ node ] }) : node;
9051            }));
9052        });
9053        def(AST_ObjectIdentity, return_null);
9054        def(AST_Sequence, function(compressor, first_in_statement) {
9055            var expressions = trim(this.expressions, compressor, first_in_statement);
9056            if (!expressions) return null;
9057            var end = expressions.length - 1;
9058            var last = expressions[end];
9059            if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) {
9060                expressions = expressions.slice(0, -1);
9061                end--;
9062                var expr = expressions[end];
9063                last.expression = needs_enqueuing(compressor, expr) ? expr : expr.negate(compressor);
9064                expressions[end] = last;
9065            }
9066            var assign, cond, lhs;
9067            if (compressor.option("conditionals")
9068                && end > 0
9069                && (assign = expressions[end - 1]) instanceof AST_Assign
9070                && assign.operator == "="
9071                && (lhs = assign.left) instanceof AST_SymbolRef
9072                && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) {
9073                assign = assign.clone();
9074                assign.right = cond;
9075                expressions = expressions.slice(0, -2);
9076                expressions.push(assign.drop_side_effect_free(compressor, first_in_statement));
9077            }
9078            return expressions === this.expressions ? this : make_sequence(this, expressions);
9079        });
9080        def(AST_Sub, function(compressor, first_in_statement) {
9081            var expr = this.expression;
9082            if (expr.may_throw_on_access(compressor)) return this;
9083            var prop = this.property;
9084            expr = expr.drop_side_effect_free(compressor, first_in_statement);
9085            if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement);
9086            prop = prop.drop_side_effect_free(compressor);
9087            if (!prop) return expr;
9088            return make_sequence(this, [ expr, prop ]);
9089        });
9090        def(AST_SymbolRef, function(compressor) {
9091            return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
9092        });
9093        def(AST_Template, function(compressor, first_in_statement) {
9094            var self = this;
9095            if (self.is_expr_pure(compressor)) {
9096                var expressions = self.expressions;
9097                if (expressions.length == 0) return null;
9098                return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
9099            }
9100            var tag = self.tag;
9101            var dropped = drop_returns(compressor, tag);
9102            if (dropped) {
9103                // always shallow clone to signal internal changes
9104                self = self.clone();
9105                self.tag = dropped;
9106                // avoid extraneous traversal
9107                if (tag._squeezed) self.tag._squeezed = true;
9108            }
9109            return self;
9110        });
9111        def(AST_Unary, function(compressor, first_in_statement) {
9112            var exp = this.expression;
9113            if (unary_side_effects[this.operator]) {
9114                this.write_only = !exp.has_side_effects(compressor);
9115                return this;
9116            }
9117            if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) {
9118                return null;
9119            }
9120            var node = exp.drop_side_effect_free(compressor, first_in_statement);
9121            if (first_in_statement && node && is_iife_call(node)) {
9122                if (node === exp && this.operator == "!") return this;
9123                return node.negate(compressor, first_in_statement);
9124            }
9125            return node;
9126        });
9127    })(function(node, func) {
9128        node.DEFMETHOD("drop_side_effect_free", func);
9129    });
9130
9131    OPT(AST_SimpleStatement, function(self, compressor) {
9132        if (compressor.option("side_effects")) {
9133            var body = self.body;
9134            var node = body.drop_side_effect_free(compressor, true);
9135            if (!node) {
9136                AST_Node.warn("Dropping side-effect-free statement [{start}]", self);
9137                return make_node(AST_EmptyStatement, self);
9138            }
9139            if (node !== body) {
9140                return make_node(AST_SimpleStatement, self, { body: node });
9141            }
9142        }
9143        return self;
9144    });
9145
9146    OPT(AST_While, function(self, compressor) {
9147        return compressor.option("loops") ? make_node(AST_For, self).optimize(compressor) : self;
9148    });
9149
9150    function has_loop_control(loop, parent, type) {
9151        if (!type) type = AST_LoopControl;
9152        var found = false;
9153        var tw = new TreeWalker(function(node) {
9154            if (found || node instanceof AST_Scope) return true;
9155            if (node instanceof type && tw.loopcontrol_target(node) === loop) {
9156                return found = true;
9157            }
9158        });
9159        if (parent instanceof AST_LabeledStatement) tw.push(parent);
9160        tw.push(loop);
9161        loop.body.walk(tw);
9162        return found;
9163    }
9164
9165    OPT(AST_Do, function(self, compressor) {
9166        if (!compressor.option("loops")) return self;
9167        var cond = fuzzy_eval(compressor, self.condition);
9168        if (!(cond instanceof AST_Node)) {
9169            if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, {
9170                body: make_node(AST_BlockStatement, self.body, {
9171                    body: [
9172                        self.body,
9173                        make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
9174                    ],
9175                }),
9176            }).optimize(compressor);
9177            if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, {
9178                body: [
9179                    self.body,
9180                    make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
9181                ],
9182            }).optimize(compressor);
9183        }
9184        if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
9185            var body = self.body.body;
9186            for (var i = body.length; --i >= 0;) {
9187                var stat = body[i];
9188                if (stat instanceof AST_If
9189                    && !stat.alternative
9190                    && stat.body instanceof AST_Break
9191                    && compressor.loopcontrol_target(stat.body) === self) {
9192                    if (has_block_scope_refs(stat.condition)) break;
9193                    self.condition = make_node(AST_Binary, self, {
9194                        operator: "&&",
9195                        left: stat.condition.negate(compressor),
9196                        right: self.condition,
9197                    });
9198                    body.splice(i, 1);
9199                } else if (stat instanceof AST_SimpleStatement) {
9200                    if (has_block_scope_refs(stat.body)) break;
9201                    self.condition = make_sequence(self, [
9202                        stat.body,
9203                        self.condition,
9204                    ]);
9205                    body.splice(i, 1);
9206                } else if (!is_declaration(stat, true)) {
9207                    break;
9208                }
9209            }
9210            self.body = trim_block(self.body, compressor.parent());
9211        }
9212        if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self).optimize(compressor);
9213        if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
9214            condition: make_sequence(self.condition, [
9215                self.body.body,
9216                self.condition,
9217            ]),
9218            body: make_node(AST_EmptyStatement, self),
9219        }).optimize(compressor);
9220        return self;
9221
9222        function has_block_scope_refs(node) {
9223            var found = false;
9224            node.walk(new TreeWalker(function(node) {
9225                if (found) return true;
9226                if (node instanceof AST_SymbolRef) {
9227                    if (!member(node.definition(), self.enclosed)) found = true;
9228                    return true;
9229                }
9230            }));
9231            return found;
9232        }
9233    });
9234
9235    function if_break_in_loop(self, compressor) {
9236        var first = first_statement(self.body);
9237        if (compressor.option("dead_code")
9238            && (first instanceof AST_Break
9239                || first instanceof AST_Continue && external_target(first)
9240                || first instanceof AST_Exit)) {
9241            var body = [];
9242            if (is_statement(self.init)) {
9243                body.push(self.init);
9244            } else if (self.init) {
9245                body.push(make_node(AST_SimpleStatement, self.init, { body: self.init }));
9246            }
9247            var retain = external_target(first) || first instanceof AST_Exit;
9248            if (self.condition && retain) {
9249                body.push(make_node(AST_If, self, {
9250                    condition: self.condition,
9251                    body: first,
9252                    alternative: null,
9253                }));
9254            } else if (self.condition) {
9255                body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition }));
9256            } else if (retain) {
9257                body.push(first);
9258            }
9259            extract_declarations_from_unreachable_code(compressor, self.body, body);
9260            return make_node(AST_BlockStatement, self, { body: body });
9261        }
9262        if (first instanceof AST_If) {
9263            var ab = first_statement(first.body);
9264            if (ab instanceof AST_Break && !external_target(ab)) {
9265                if (self.condition) {
9266                    self.condition = make_node(AST_Binary, self.condition, {
9267                        left: self.condition,
9268                        operator: "&&",
9269                        right: first.condition.negate(compressor),
9270                    });
9271                } else {
9272                    self.condition = first.condition.negate(compressor);
9273                }
9274                var body = as_statement_array(first.alternative);
9275                extract_declarations_from_unreachable_code(compressor, first.body, body);
9276                return drop_it(body);
9277            }
9278            ab = first_statement(first.alternative);
9279            if (ab instanceof AST_Break && !external_target(ab)) {
9280                if (self.condition) {
9281                    self.condition = make_node(AST_Binary, self.condition, {
9282                        left: self.condition,
9283                        operator: "&&",
9284                        right: first.condition,
9285                    });
9286                } else {
9287                    self.condition = first.condition;
9288                }
9289                var body = as_statement_array(first.body);
9290                extract_declarations_from_unreachable_code(compressor, first.alternative, body);
9291                return drop_it(body);
9292            }
9293        }
9294        return self;
9295
9296        function first_statement(body) {
9297            return body instanceof AST_BlockStatement ? body.body[0] : body;
9298        }
9299
9300        function external_target(node) {
9301            return compressor.loopcontrol_target(node) !== compressor.self();
9302        }
9303
9304        function drop_it(rest) {
9305            if (self.body instanceof AST_BlockStatement) {
9306                self.body = self.body.clone();
9307                self.body.body = rest.concat(self.body.body.slice(1));
9308                self.body = self.body.transform(compressor);
9309            } else {
9310                self.body = make_node(AST_BlockStatement, self.body, { body: rest }).transform(compressor);
9311            }
9312            return if_break_in_loop(self, compressor);
9313        }
9314    }
9315
9316    OPT(AST_For, function(self, compressor) {
9317        if (!compressor.option("loops")) return self;
9318        if (compressor.option("side_effects")) {
9319            if (self.init) self.init = self.init.drop_side_effect_free(compressor);
9320            if (self.step) self.step = self.step.drop_side_effect_free(compressor);
9321        }
9322        if (self.condition) {
9323            var cond = fuzzy_eval(compressor, self.condition);
9324            if (!cond) {
9325                if (compressor.option("dead_code")) {
9326                    var body = [];
9327                    if (is_statement(self.init)) {
9328                        body.push(self.init);
9329                    } else if (self.init) {
9330                        body.push(make_node(AST_SimpleStatement, self.init, { body: self.init }));
9331                    }
9332                    body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition }));
9333                    extract_declarations_from_unreachable_code(compressor, self.body, body);
9334                    return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
9335                }
9336            } else if (!(cond instanceof AST_Node)) {
9337                self.body = make_node(AST_BlockStatement, self.body, {
9338                    body: [
9339                        make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
9340                        self.body,
9341                    ],
9342                });
9343                self.condition = null;
9344            }
9345        }
9346        return if_break_in_loop(self, compressor);
9347    });
9348
9349    OPT(AST_ForEnumeration, function(self, compressor) {
9350        if (compressor.option("varify") && is_lexical_definition(self.init)) {
9351            var name = self.init.definitions[0].name;
9352            if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
9353                && !name.match_symbol(function(node) {
9354                    if (node instanceof AST_SymbolDeclaration) {
9355                        var def = node.definition();
9356                        return !same_scope(def) || may_overlap(compressor, def);
9357                    }
9358                }, true)) {
9359                self.init = to_var(self.init, self.resolve());
9360            }
9361        }
9362        return self;
9363    });
9364
9365    function mark_locally_defined(condition, consequent, alternative) {
9366        if (condition instanceof AST_Sequence) condition = condition.tail_node();
9367        if (!(condition instanceof AST_Binary)) return;
9368        if (!(condition.left instanceof AST_String)) {
9369            switch (condition.operator) {
9370              case "&&":
9371                mark_locally_defined(condition.left, consequent);
9372                mark_locally_defined(condition.right, consequent);
9373                break;
9374              case "||":
9375                mark_locally_defined(negate(condition.left), alternative);
9376                mark_locally_defined(negate(condition.right), alternative);
9377                break;
9378            }
9379            return;
9380        }
9381        if (!(condition.right instanceof AST_UnaryPrefix)) return;
9382        if (condition.right.operator != "typeof") return;
9383        var sym = condition.right.expression;
9384        if (!is_undeclared_ref(sym)) return;
9385        var body;
9386        var undef = condition.left.value == "undefined";
9387        switch (condition.operator) {
9388          case "==":
9389            body = undef ? alternative : consequent;
9390            break;
9391          case "!=":
9392            body = undef ? consequent : alternative;
9393            break;
9394          default:
9395            return;
9396        }
9397        if (!body) return;
9398        var abort = false;
9399        var def = sym.definition();
9400        var fn;
9401        var refs = [];
9402        var scanned = [];
9403        var tw = new TreeWalker(function(node, descend) {
9404            if (abort) return true;
9405            if (node instanceof AST_Assign) {
9406                var ref = node.left;
9407                if (!(ref instanceof AST_SymbolRef && ref.definition() === def)) return;
9408                node.right.walk(tw);
9409                switch (node.operator) {
9410                  case "=":
9411                  case "&&=":
9412                    abort = true;
9413                }
9414                return true;
9415            }
9416            if (node instanceof AST_Call) {
9417                descend();
9418                fn = node.expression.tail_node();
9419                var save;
9420                if (fn instanceof AST_SymbolRef) {
9421                    fn = fn.fixed_value();
9422                    save = refs.length;
9423                }
9424                if (!(fn instanceof AST_Lambda)) {
9425                    abort = true;
9426                } else if (push_uniq(scanned, fn)) {
9427                    fn.walk(tw);
9428                }
9429                if (save >= 0) refs.length = save;
9430                return true;
9431            }
9432            if (node instanceof AST_DWLoop) {
9433                var save = refs.length;
9434                descend();
9435                if (abort) refs.length = save;
9436                return true;
9437            }
9438            if (node instanceof AST_For) {
9439                if (node.init) node.init.walk(tw);
9440                var save = refs.length;
9441                if (node.condition) node.condition.walk(tw);
9442                node.body.walk(tw);
9443                if (node.step) node.step.walk(tw);
9444                if (abort) refs.length = save;
9445                return true;
9446            }
9447            if (node instanceof AST_ForEnumeration) {
9448                node.object.walk(tw);
9449                var save = refs.length;
9450                node.init.walk(tw);
9451                node.body.walk(tw);
9452                if (abort) refs.length = save;
9453                return true;
9454            }
9455            if (node instanceof AST_Scope) {
9456                if (node === fn) return;
9457                return true;
9458            }
9459            if (node instanceof AST_SymbolRef) {
9460                if (node.definition() === def) refs.push(node);
9461                return true;
9462            }
9463        });
9464        body.walk(tw);
9465        refs.forEach(function(ref) {
9466            ref.defined = true;
9467        });
9468
9469        function negate(node) {
9470            if (!(node instanceof AST_Binary)) return;
9471            switch (node.operator) {
9472              case "==":
9473                node = node.clone();
9474                node.operator = "!=";
9475                return node;
9476              case "!=":
9477                node = node.clone();
9478                node.operator = "==";
9479                return node;
9480            }
9481        }
9482    }
9483
9484    function fuzzy_eval(compressor, node, nullish) {
9485        if (node.truthy) return true;
9486        if (is_undefined(node)) return undefined;
9487        if (node.falsy && !nullish) return false;
9488        if (node.is_truthy()) return true;
9489        return node.evaluate(compressor, true);
9490    }
9491
9492    function mark_duplicate_condition(compressor, node) {
9493        var child;
9494        var level = 0;
9495        var negated = false;
9496        var parent = compressor.self();
9497        if (!is_statement(parent)) while (true) {
9498            child = parent;
9499            parent = compressor.parent(level++);
9500            if (parent instanceof AST_Binary) {
9501                switch (child) {
9502                  case parent.left:
9503                    if (lazy_op[parent.operator]) continue;
9504                    break;
9505                  case parent.right:
9506                    if (match(parent.left)) switch (parent.operator) {
9507                      case "&&":
9508                        node[negated ? "falsy" : "truthy"] = true;
9509                        break;
9510                      case "||":
9511                      case "??":
9512                        node[negated ? "truthy" : "falsy"] = true;
9513                        break;
9514                    }
9515                    break;
9516                }
9517            } else if (parent instanceof AST_Conditional) {
9518                var cond = parent.condition;
9519                if (cond === child) continue;
9520                if (match(cond)) switch (child) {
9521                  case parent.consequent:
9522                    node[negated ? "falsy" : "truthy"] = true;
9523                    break;
9524                  case parent.alternative:
9525                    node[negated ? "truthy" : "falsy"] = true;
9526                    break;
9527                }
9528            } else if (parent instanceof AST_Exit) {
9529                break;
9530            } else if (parent instanceof AST_If) {
9531                break;
9532            } else if (parent instanceof AST_Sequence) {
9533                if (parent.expressions[0] === child) continue;
9534            } else if (parent instanceof AST_SimpleStatement) {
9535                break;
9536            }
9537            return;
9538        }
9539        while (true) {
9540            child = parent;
9541            parent = compressor.parent(level++);
9542            if (parent instanceof AST_BlockStatement) {
9543                if (parent.body[0] === child) continue;
9544            } else if (parent instanceof AST_If) {
9545                if (match(parent.condition)) switch (child) {
9546                  case parent.body:
9547                    node[negated ? "falsy" : "truthy"] = true;
9548                    break;
9549                  case parent.alternative:
9550                    node[negated ? "truthy" : "falsy"] = true;
9551                    break;
9552                }
9553            }
9554            return;
9555        }
9556
9557        function match(cond) {
9558            if (node.equals(cond)) return true;
9559            if (!(cond instanceof AST_UnaryPrefix)) return false;
9560            if (cond.operator != "!") return false;
9561            if (!node.equals(cond.expression)) return false;
9562            negated = true;
9563            return true;
9564        }
9565    }
9566
9567    OPT(AST_If, function(self, compressor) {
9568        if (is_empty(self.alternative)) self.alternative = null;
9569
9570        if (!compressor.option("conditionals")) return self;
9571        if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) {
9572            mark_duplicate_condition(compressor, self.condition);
9573        }
9574        // if condition can be statically determined, warn and drop
9575        // one of the blocks.  note, statically determined implies
9576        // “has no side effects”; also it doesn't work for cases like
9577        // `x && true`, though it probably should.
9578        if (compressor.option("dead_code")) {
9579            var cond = fuzzy_eval(compressor, self.condition);
9580            if (!cond) {
9581                AST_Node.warn("Condition always false [{start}]", self.condition);
9582                var body = [
9583                    make_node(AST_SimpleStatement, self.condition, { body: self.condition }).transform(compressor),
9584                ];
9585                extract_declarations_from_unreachable_code(compressor, self.body, body);
9586                if (self.alternative) body.push(self.alternative);
9587                return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
9588            } else if (!(cond instanceof AST_Node)) {
9589                AST_Node.warn("Condition always true [{start}]", self.condition);
9590                var body = [
9591                    make_node(AST_SimpleStatement, self.condition, { body: self.condition }).transform(compressor),
9592                    self.body,
9593                ];
9594                if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body);
9595                return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
9596            }
9597        }
9598        var negated = self.condition.negate(compressor);
9599        var self_condition_length = self.condition.print_to_string().length;
9600        var negated_length = negated.print_to_string().length;
9601        var negated_is_best = negated_length < self_condition_length;
9602        if (self.alternative && negated_is_best) {
9603            negated_is_best = false; // because we already do the switch here.
9604            // no need to swap values of self_condition_length and negated_length
9605            // here because they are only used in an equality comparison later on.
9606            self.condition = negated;
9607            var tmp = self.body;
9608            self.body = self.alternative;
9609            self.alternative = is_empty(tmp) ? null : tmp;
9610        }
9611        var body_defuns = [];
9612        var body_var_defs = [];
9613        var body_refs = [];
9614        var body_exprs = sequencesize(self.body, body_defuns, body_var_defs, body_refs);
9615        var alt_defuns = [];
9616        var alt_var_defs = [];
9617        var alt_refs = [];
9618        var alt_exprs = sequencesize(self.alternative, alt_defuns, alt_var_defs, alt_refs);
9619        if (body_exprs instanceof AST_BlockStatement || alt_exprs instanceof AST_BlockStatement) {
9620            var body = [], var_defs = [];
9621            if (body_exprs) {
9622                [].push.apply(body, body_defuns);
9623                [].push.apply(var_defs, body_var_defs);
9624                if (body_exprs instanceof AST_BlockStatement) {
9625                    self.body = body_exprs;
9626                } else if (body_exprs.length == 0) {
9627                    self.body = make_node(AST_EmptyStatement, self.body);
9628                } else {
9629                    self.body = make_node(AST_SimpleStatement, self.body, {
9630                        body: make_sequence(self.body, body_exprs),
9631                    });
9632                }
9633                body_refs.forEach(process_to_assign);
9634            }
9635            if (alt_exprs) {
9636                [].push.apply(body, alt_defuns);
9637                [].push.apply(var_defs, alt_var_defs);
9638                if (alt_exprs instanceof AST_BlockStatement) {
9639                    self.alternative = alt_exprs;
9640                } else if (alt_exprs.length == 0) {
9641                    self.alternative = null;
9642                } else {
9643                    self.alternative = make_node(AST_SimpleStatement, self.alternative, {
9644                        body: make_sequence(self.alternative, alt_exprs),
9645                    });
9646                }
9647                alt_refs.forEach(process_to_assign);
9648            }
9649            if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
9650            if (body.length > 0) {
9651                body.push(self.transform(compressor));
9652                return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
9653            }
9654        } else if (body_exprs && alt_exprs) {
9655            var body = body_defuns.concat(alt_defuns);
9656            if (body_var_defs.length > 0 || alt_var_defs.length > 0) body.push(make_node(AST_Var, self, {
9657                definitions: body_var_defs.concat(alt_var_defs),
9658            }));
9659            if (body_exprs.length == 0) {
9660                body.push(make_node(AST_SimpleStatement, self.condition, {
9661                    body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
9662                        operator: "||",
9663                        left: self.condition,
9664                        right: make_sequence(self.alternative, alt_exprs),
9665                    }).transform(compressor) : self.condition.clone(),
9666                }).optimize(compressor));
9667            } else if (alt_exprs.length == 0) {
9668                if (self_condition_length === negated_length && !negated_is_best
9669                    && self.condition instanceof AST_Binary && self.condition.operator == "||") {
9670                    // although the code length of self.condition and negated are the same,
9671                    // negated does not require additional surrounding parentheses.
9672                    // see https://github.com/mishoo/UglifyJS/issues/979
9673                    negated_is_best = true;
9674                }
9675                body.push(make_node(AST_SimpleStatement, self, {
9676                    body: make_node(AST_Binary, self, {
9677                        operator: negated_is_best ? "||" : "&&",
9678                        left: negated_is_best ? negated : self.condition,
9679                        right: make_sequence(self.body, body_exprs),
9680                    }).transform(compressor),
9681                }).optimize(compressor));
9682            } else {
9683                body.push(make_node(AST_SimpleStatement, self, {
9684                    body: make_node(AST_Conditional, self, {
9685                        condition: self.condition,
9686                        consequent: make_sequence(self.body, body_exprs),
9687                        alternative: make_sequence(self.alternative, alt_exprs),
9688                    }),
9689                }).optimize(compressor));
9690            }
9691            body_refs.forEach(process_to_assign);
9692            alt_refs.forEach(process_to_assign);
9693            return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
9694        }
9695        if (is_empty(self.body)) self = make_node(AST_If, self, {
9696            condition: negated,
9697            body: self.alternative,
9698            alternative: null,
9699        });
9700        if (self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) {
9701            var cons_value = self.body.value;
9702            var alt_value = self.alternative.value;
9703            if (!cons_value && !alt_value) return make_node(AST_BlockStatement, self, {
9704                body: [
9705                    make_node(AST_SimpleStatement, self, { body: self.condition }),
9706                    self.body,
9707                ],
9708            }).optimize(compressor);
9709            if (cons_value && alt_value || !keep_return_void()) {
9710                var exit = make_node(self.body.CTOR, self, {
9711                    value: make_node(AST_Conditional, self, {
9712                        condition: self.condition,
9713                        consequent: cons_value || make_node(AST_Undefined, self.body).transform(compressor),
9714                        alternative: alt_value || make_node(AST_Undefined, self.alternative).transform(compressor),
9715                    }),
9716                });
9717                if (exit instanceof AST_Return) exit.in_bool = self.body.in_bool || self.alternative.in_bool;
9718                return exit;
9719            }
9720        }
9721        if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) {
9722            self = make_node(AST_If, self, {
9723                condition: make_node(AST_Binary, self.condition, {
9724                    operator: "&&",
9725                    left: self.condition,
9726                    right: self.body.condition,
9727                }),
9728                body: self.body.body,
9729                alternative: null,
9730            });
9731        }
9732        if (aborts(self.body) && self.alternative) {
9733            var alt = self.alternative;
9734            self.alternative = null;
9735            return make_node(AST_BlockStatement, self, { body: [ self, alt ] }).optimize(compressor);
9736        }
9737        if (aborts(self.alternative)) {
9738            var body = self.body;
9739            self.body = self.alternative;
9740            self.condition = negated_is_best ? negated : self.condition.negate(compressor);
9741            self.alternative = null;
9742            return make_node(AST_BlockStatement, self, { body: [ self, body ] }).optimize(compressor);
9743        }
9744        if (self.alternative) {
9745            var body_stats = as_array(self.body);
9746            var body_index = last_index(body_stats);
9747            var alt_stats = as_array(self.alternative);
9748            var alt_index = last_index(alt_stats);
9749            for (var stats = []; body_index >= 0 && alt_index >= 0;) {
9750                var stat = body_stats[body_index];
9751                var alt_stat = alt_stats[alt_index];
9752                if (stat.equals(alt_stat)) {
9753                    body_stats.splice(body_index--, 1);
9754                    alt_stats.splice(alt_index--, 1);
9755                    stats.unshift(merge_expression(stat, alt_stat));
9756                } else {
9757                    if (!(stat instanceof AST_SimpleStatement)) break;
9758                    if (!(alt_stat instanceof AST_SimpleStatement)) break;
9759                    var expr1 = stat.body.tail_node();
9760                    var expr2 = alt_stat.body.tail_node();
9761                    if (!expr1.equals(expr2)) break;
9762                    body_index = pop_expr(body_stats, stat.body, body_index);
9763                    alt_index = pop_expr(alt_stats, alt_stat.body, alt_index);
9764                    stats.unshift(make_node(AST_SimpleStatement, expr1, { body: merge_expression(expr1, expr2) }));
9765                }
9766            }
9767            if (stats.length > 0) {
9768                self.body = body_stats.length > 0 ? make_node(AST_BlockStatement, self, {
9769                    body: body_stats,
9770                }) : make_node(AST_EmptyStatement, self);
9771                self.alternative = alt_stats.length > 0 ? make_node(AST_BlockStatement, self, {
9772                    body: alt_stats,
9773                }) : null;
9774                stats.unshift(self);
9775                return make_node(AST_BlockStatement, self, { body: stats }).optimize(compressor);
9776            }
9777        }
9778        if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
9779        return self;
9780
9781        function as_array(node) {
9782            return node instanceof AST_BlockStatement ? node.body : [ node ];
9783        }
9784
9785        function keep_return_void() {
9786            var has_finally = false, level = 0, node = compressor.self();
9787            do {
9788                if (node instanceof AST_Catch) {
9789                    if (compressor.parent(level).bfinally) has_finally = true;
9790                    level++;
9791                } else if (node instanceof AST_Finally) {
9792                    level++;
9793                } else if (node instanceof AST_Scope) {
9794                    return has_finally && in_async_generator(node);
9795                } else if (node instanceof AST_Try) {
9796                    if (node.bfinally) has_finally = true;
9797                }
9798            } while (node = compressor.parent(level++));
9799        }
9800
9801        function last_index(stats) {
9802            for (var index = stats.length; --index >= 0;) {
9803                if (!is_declaration(stats[index], true)) break;
9804            }
9805            return index;
9806        }
9807
9808        function pop_expr(stats, body, index) {
9809            if (body instanceof AST_Sequence) {
9810                stats[index] = make_node(AST_SimpleStatement, body, {
9811                    body: make_sequence(body, body.expressions.slice(0, -1)),
9812                });
9813            } else {
9814                stats.splice(index--, 1);
9815            }
9816            return index;
9817        }
9818
9819        function sequencesize(stat, defuns, var_defs, refs) {
9820            if (stat == null) return [];
9821            if (stat instanceof AST_BlockStatement) {
9822                var exprs = [];
9823                for (var i = 0; i < stat.body.length; i++) {
9824                    var line = stat.body[i];
9825                    if (line instanceof AST_EmptyStatement) continue;
9826                    if (line instanceof AST_Exit) {
9827                        if (i == 0) return;
9828                        if (exprs.length > 0) {
9829                            line = line.clone();
9830                            exprs.push(line.value || make_node(AST_Undefined, line).transform(compressor));
9831                            line.value = make_sequence(stat, exprs);
9832                        }
9833                        var block = stat.clone();
9834                        block.body = block.body.slice(i + 1);
9835                        block.body.unshift(line);
9836                        return block;
9837                    }
9838                    if (line instanceof AST_LambdaDefinition) {
9839                        defuns.push(line);
9840                    } else if (line instanceof AST_SimpleStatement) {
9841                        if (!compressor.option("sequences") && exprs.length > 0) return;
9842                        exprs.push(line.body);
9843                    } else if (line instanceof AST_Var) {
9844                        if (!compressor.option("sequences") && exprs.length > 0) return;
9845                        line.remove_initializers(compressor, var_defs);
9846                        line.definitions.forEach(process_var_def);
9847                    } else {
9848                        return;
9849                    }
9850                }
9851                return exprs;
9852            }
9853            if (stat instanceof AST_LambdaDefinition) {
9854                defuns.push(stat);
9855                return [];
9856            }
9857            if (stat instanceof AST_EmptyStatement) return [];
9858            if (stat instanceof AST_SimpleStatement) return [ stat.body ];
9859            if (stat instanceof AST_Var) {
9860                var exprs = [];
9861                stat.remove_initializers(compressor, var_defs);
9862                stat.definitions.forEach(process_var_def);
9863                return exprs;
9864            }
9865
9866            function process_var_def(var_def) {
9867                if (!var_def.value) return;
9868                exprs.push(make_node(AST_Assign, var_def, {
9869                    operator: "=",
9870                    left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) {
9871                        refs.push(ref);
9872                    }),
9873                    right: var_def.value,
9874                }));
9875            }
9876        }
9877    });
9878
9879    OPT(AST_Switch, function(self, compressor) {
9880        if (!compressor.option("switches")) return self;
9881        if (!compressor.option("dead_code")) return self;
9882        var body = [];
9883        var branch;
9884        var decl = [];
9885        var default_branch;
9886        var exact_match;
9887        var side_effects = [];
9888        for (var i = 0, len = self.body.length; i < len; i++) {
9889            branch = self.body[i];
9890            if (branch instanceof AST_Default) {
9891                var prev = body[body.length - 1];
9892                if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
9893                    eliminate_branch(branch, prev);
9894                    continue;
9895                } else {
9896                    default_branch = branch;
9897                }
9898            } else {
9899                var exp = branch.expression;
9900                var equals = make_node(AST_Binary, self, {
9901                    operator: "===",
9902                    left: self.expression,
9903                    right: exp,
9904                }).evaluate(compressor, true);
9905                if (!equals) {
9906                    if (exp.has_side_effects(compressor)) side_effects.push(exp);
9907                    eliminate_branch(branch, body[body.length - 1]);
9908                    continue;
9909                }
9910                if (!(equals instanceof AST_Node)) {
9911                    if (default_branch) {
9912                        var default_index = body.indexOf(default_branch);
9913                        body.splice(default_index, 1);
9914                        eliminate_branch(default_branch, body[default_index - 1]);
9915                        default_branch = null;
9916                    }
9917                    if (exp.has_side_effects(compressor)) {
9918                        exact_match = branch;
9919                    } else {
9920                        default_branch = branch = make_node(AST_Default, branch);
9921                    }
9922                    while (++i < len) eliminate_branch(self.body[i], branch);
9923                }
9924            }
9925            if (i + 1 >= len || aborts(branch)) {
9926                var prev = body[body.length - 1];
9927                var statements = branch.body;
9928                if (aborts(prev)) switch (prev.body.length - statements.length) {
9929                  case 1:
9930                    var stat = prev.body[prev.body.length - 1];
9931                    if (!is_break(stat, compressor)) break;
9932                    statements = statements.concat(stat);
9933                  case 0:
9934                    var prev_block = make_node(AST_BlockStatement, prev);
9935                    var next_block = make_node(AST_BlockStatement, branch, { body: statements });
9936                    if (prev_block.equals(next_block)) prev.body = [];
9937                }
9938            }
9939            if (side_effects.length) {
9940                if (branch instanceof AST_Default) {
9941                    body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
9942                } else {
9943                    side_effects.push(branch.expression);
9944                    branch.expression = make_sequence(self, side_effects);
9945                }
9946                side_effects = [];
9947            }
9948            body.push(branch);
9949        }
9950        if (side_effects.length && !exact_match) {
9951            body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
9952        }
9953        while (branch = body[body.length - 1]) {
9954            var stat = branch.body[branch.body.length - 1];
9955            if (is_break(stat, compressor)) branch.body.pop();
9956            if (branch === default_branch) {
9957                if (!has_declarations_only(branch)) break;
9958            } else if (branch.expression.has_side_effects(compressor)) {
9959                break;
9960            } else if (default_branch) {
9961                if (!has_declarations_only(default_branch)) break;
9962                if (body[body.length - 2] !== default_branch) break;
9963                default_branch.body = default_branch.body.concat(branch.body);
9964                branch.body = [];
9965            } else if (!has_declarations_only(branch)) break;
9966            eliminate_branch(branch);
9967            if (body.pop() === default_branch) default_branch = null;
9968        }
9969        if (!branch) {
9970            decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
9971            if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, {
9972                body: make_sequence(self, side_effects),
9973            }));
9974            return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor);
9975        }
9976        if (branch === default_branch) while (branch = body[body.length - 2]) {
9977            if (branch instanceof AST_Default) break;
9978            if (!has_declarations_only(branch)) break;
9979            var exp = branch.expression;
9980            if (exp.has_side_effects(compressor)) {
9981                var prev = body[body.length - 3];
9982                if (prev && !aborts(prev)) break;
9983                default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp }));
9984            }
9985            eliminate_branch(branch);
9986            body.splice(-2, 1);
9987        }
9988        body[0].body = decl.concat(body[0].body);
9989        self.body = body;
9990        if (compressor.option("conditionals")) switch (body.length) {
9991          case 1:
9992            if (!no_break(body[0])) break;
9993            var exp = body[0].expression;
9994            var statements = body[0].body.slice();
9995            if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
9996                condition: make_node(AST_Binary, self, {
9997                    operator: "===",
9998                    left: self.expression,
9999                    right: exp,
10000                }),
10001                body: make_node(AST_BlockStatement, self, { body: statements }),
10002                alternative: null,
10003            }).optimize(compressor);
10004            if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, { body: exp }));
10005            statements.unshift(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
10006            return make_node(AST_BlockStatement, self, { body: statements }).optimize(compressor);
10007          case 2:
10008            if (!member(default_branch, body) || !no_break(body[1])) break;
10009            var statements = body[0].body.slice();
10010            var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
10011            if (exclusive) statements.pop();
10012            if (!all(statements, no_break)) break;
10013            var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1]);
10014            var node = make_node(AST_If, self, {
10015                condition: make_node(AST_Binary, self, body[0] === default_branch ? {
10016                    operator: "!==",
10017                    left: self.expression,
10018                    right: body[1].expression,
10019                } : {
10020                    operator: "===",
10021                    left: self.expression,
10022                    right: body[0].expression,
10023                }),
10024                body: make_node(AST_BlockStatement, body[0], { body: statements }),
10025                alternative: exclusive && alternative || null,
10026            });
10027            if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, { body: [ node, alternative ] });
10028            return node.optimize(compressor);
10029        }
10030        return self;
10031
10032        function is_break(node, tw) {
10033            return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
10034        }
10035
10036        function no_break(node) {
10037            var found = false;
10038            var tw = new TreeWalker(function(node) {
10039                if (found
10040                    || node instanceof AST_Lambda
10041                    || node instanceof AST_SimpleStatement) return true;
10042                if (is_break(node, tw)) found = true;
10043            });
10044            tw.push(self);
10045            node.walk(tw);
10046            return !found;
10047        }
10048
10049        function eliminate_branch(branch, prev) {
10050            if (prev && !aborts(prev)) {
10051                prev.body = prev.body.concat(branch.body);
10052            } else {
10053                extract_declarations_from_unreachable_code(compressor, branch, decl);
10054            }
10055        }
10056    });
10057
10058    OPT(AST_Try, function(self, compressor) {
10059        self.body = tighten_body(self.body, compressor);
10060        if (compressor.option("dead_code")) {
10061            if (has_declarations_only(self)
10062                && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
10063                    return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
10064                }, true))) {
10065                var body = [];
10066                if (self.bcatch) {
10067                    extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
10068                    body.forEach(function(stat) {
10069                        if (!(stat instanceof AST_Var)) return;
10070                        stat.definitions.forEach(function(var_def) {
10071                            var def = var_def.name.definition().redefined();
10072                            if (!def) return;
10073                            var_def.name = var_def.name.clone();
10074                            var_def.name.thedef = def;
10075                        });
10076                    });
10077                }
10078                body.unshift(make_node(AST_BlockStatement, self).optimize(compressor));
10079                if (self.bfinally) {
10080                    body.push(make_node(AST_BlockStatement, self.bfinally).optimize(compressor));
10081                }
10082                return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
10083            }
10084            if (self.bfinally && has_declarations_only(self.bfinally)) {
10085                var body = make_node(AST_BlockStatement, self.bfinally).optimize(compressor);
10086                body = self.body.concat(body);
10087                if (!self.bcatch) return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
10088                self.body = body;
10089                self.bfinally = null;
10090            }
10091        }
10092        return self;
10093    });
10094
10095    function remove_initializers(make_value) {
10096        return function(compressor, defns) {
10097            var dropped = false;
10098            this.definitions.forEach(function(defn) {
10099                if (defn.value) dropped = true;
10100                defn.name.match_symbol(function(node) {
10101                    if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, {
10102                        name: node,
10103                        value: make_value(compressor, node),
10104                    }));
10105                }, true);
10106            });
10107            return dropped;
10108        };
10109    }
10110
10111    AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) {
10112        return make_node(AST_Undefined, node).optimize(compressor);
10113    }));
10114    AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null));
10115    AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null));
10116
10117    AST_Definitions.DEFMETHOD("to_assignments", function() {
10118        var assignments = this.definitions.reduce(function(a, defn) {
10119            var def = defn.name.definition();
10120            var value = defn.value;
10121            if (value) {
10122                if (value instanceof AST_Sequence) value = value.clone();
10123                var name = make_node(AST_SymbolRef, defn.name);
10124                var assign = make_node(AST_Assign, defn, {
10125                    operator: "=",
10126                    left: name,
10127                    right: value,
10128                });
10129                a.push(assign);
10130                var fixed = function() {
10131                    return assign.right;
10132                };
10133                fixed.assigns = [ assign ];
10134                fixed.direct_access = def.direct_access;
10135                fixed.escaped = def.escaped;
10136                name.fixed = fixed;
10137                def.references.forEach(function(ref) {
10138                    if (!ref.fixed) return;
10139                    var assigns = ref.fixed.assigns;
10140                    if (!assigns) return;
10141                    if (assigns[0] !== defn) return;
10142                    if (assigns.length > 1 || ref.fixed.to_binary || ref.fixed.to_prefix) {
10143                        assigns[0] = assign;
10144                    } else {
10145                        ref.fixed = fixed;
10146                        if (def.fixed === ref.fixed) def.fixed = fixed;
10147                    }
10148                });
10149                def.references.push(name);
10150            }
10151            def.assignments++;
10152            def.eliminated++;
10153            def.single_use = false;
10154            return a;
10155        }, []);
10156        if (assignments.length == 0) return null;
10157        return make_sequence(this, assignments);
10158    });
10159
10160    function is_safe_lexical(def) {
10161        return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
10162    }
10163
10164    function may_overlap(compressor, def) {
10165        if (compressor.exposed(def)) return true;
10166        var scope = def.scope.resolve();
10167        for (var s = def.scope; s !== scope;) {
10168            s = s.parent_scope;
10169            if (s.var_names().has(def.name)) return true;
10170        }
10171    }
10172
10173    function to_var(stat, scope) {
10174        return make_node(AST_Var, stat, {
10175            definitions: stat.definitions.map(function(defn) {
10176                return make_node(AST_VarDef, defn, {
10177                    name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
10178                        var def = name.definition();
10179                        def.orig[def.orig.indexOf(node)] = name;
10180                        if (def.scope === scope) return;
10181                        def.scope = scope;
10182                        scope.variables.set(def.name, def);
10183                        scope.enclosed.push(def);
10184                        scope.var_names().set(def.name, true);
10185                    }),
10186                    value: defn.value,
10187                });
10188            }),
10189        });
10190    }
10191
10192    function can_varify(compressor, sym) {
10193        var def = sym.definition();
10194        return (def.fixed || def.fixed === 0)
10195            && is_safe_lexical(def)
10196            && same_scope(def)
10197            && !may_overlap(compressor, def);
10198    }
10199
10200    function varify(self, compressor) {
10201        return compressor.option("varify") && all(self.definitions, function(defn) {
10202            return !defn.name.match_symbol(function(node) {
10203                if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
10204            }, true);
10205        }) ? to_var(self, compressor.find_parent(AST_Scope)) : self;
10206    }
10207
10208    OPT(AST_Const, varify);
10209    OPT(AST_Let, varify);
10210
10211    function trim_optional_chain(node, compressor) {
10212        if (!compressor.option("optional_chains")) return;
10213        if (node.terminal) do {
10214            var expr = node.expression;
10215            if (node.optional) {
10216                var ev = fuzzy_eval(compressor, expr, true);
10217                if (ev == null) return make_node(AST_UnaryPrefix, node, {
10218                    operator: "void",
10219                    expression: expr,
10220                }).optimize(compressor);
10221                if (!(ev instanceof AST_Node)) node.optional = false;
10222            }
10223            node = expr;
10224        } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal);
10225    }
10226
10227    function lift_sequence_in_expression(node, compressor) {
10228        var exp = node.expression;
10229        if (!(exp instanceof AST_Sequence)) return node;
10230        var x = exp.expressions.slice();
10231        var e = node.clone();
10232        e.expression = x.pop();
10233        x.push(e);
10234        return make_sequence(node, x);
10235    }
10236
10237    function drop_unused_call_args(call, compressor, fns_with_marked_args) {
10238        var exp = call.expression;
10239        var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
10240        if (!(fn instanceof AST_Lambda)) return;
10241        if (fn.uses_arguments) return;
10242        if (fn.pinned()) return;
10243        if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
10244        var args = call.args;
10245        if (!all(args, function(arg) {
10246            return !(arg instanceof AST_Spread);
10247        })) return;
10248        var argnames = fn.argnames;
10249        var is_iife = fn === exp && !fn.name;
10250        if (fn.rest) {
10251            if (!(is_iife && compressor.option("rests"))) return;
10252            var insert = argnames.length;
10253            args = args.slice(0, insert);
10254            while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor));
10255            args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) }));
10256            argnames = argnames.concat(fn.rest);
10257            fn.rest = null;
10258        } else {
10259            args = args.slice();
10260            argnames = argnames.slice();
10261        }
10262        var pos = 0, last = 0;
10263        var drop_defaults = is_iife && compressor.option("default_values");
10264        var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
10265            if (!argname) return true;
10266            if (argname instanceof AST_DestructuredArray) {
10267                return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
10268            }
10269            if (argname instanceof AST_DestructuredObject) {
10270                return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
10271            }
10272            return argname.unused;
10273        } : return_false;
10274        var side_effects = [];
10275        for (var i = 0; i < args.length; i++) {
10276            var argname = argnames[i];
10277            if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
10278                argnames[i] = argname = argname.name;
10279            }
10280            if (!argname || argname.unused !== undefined) {
10281                var node = args[i].drop_side_effect_free(compressor);
10282                if (drop_fargs(argname)) {
10283                    if (argname) argnames.splice(i, 1);
10284                    args.splice(i, 1);
10285                    if (node) side_effects.push(node);
10286                    i--;
10287                    continue;
10288                } else if (node) {
10289                    side_effects.push(node);
10290                    args[pos++] = make_sequence(call, side_effects);
10291                    side_effects = [];
10292                } else if (argname) {
10293                    if (side_effects.length) {
10294                        args[pos++] = make_sequence(call, side_effects);
10295                        side_effects = [];
10296                    } else {
10297                        args[pos++] = make_node(AST_Number, args[i], { value: 0 });
10298                        continue;
10299                    }
10300                }
10301            } else if (drop_fargs(argname, args[i])) {
10302                var node = args[i].drop_side_effect_free(compressor);
10303                argnames.splice(i, 1);
10304                args.splice(i, 1);
10305                if (node) side_effects.push(node);
10306                i--;
10307                continue;
10308            } else {
10309                side_effects.push(args[i]);
10310                args[pos++] = make_sequence(call, side_effects);
10311                side_effects = [];
10312            }
10313            last = pos;
10314        }
10315        for (; i < argnames.length; i++) {
10316            if (drop_fargs(argnames[i])) argnames.splice(i--, 1);
10317        }
10318        fn.argnames = argnames;
10319        args.length = last;
10320        call.args = args;
10321        if (!side_effects.length) return;
10322        var arg = make_sequence(call, side_effects);
10323        args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, {
10324            operator: "void",
10325            expression: arg,
10326        }) : arg);
10327    }
10328
10329    function avoid_await_yield(compressor, parent_scope) {
10330        if (!parent_scope) parent_scope = compressor.find_parent(AST_Scope);
10331        var avoid = [];
10332        if (is_async(parent_scope) || parent_scope instanceof AST_Toplevel && compressor.option("module")) {
10333            avoid.push("await");
10334        }
10335        if (is_generator(parent_scope)) avoid.push("yield");
10336        return avoid.length && makePredicate(avoid);
10337    }
10338
10339    function safe_from_await_yield(fn, avoid) {
10340        if (!avoid) return true;
10341        var safe = true;
10342        var tw = new TreeWalker(function(node) {
10343            if (!safe) return true;
10344            if (node instanceof AST_Scope) {
10345                if (node === fn) return;
10346                if (is_arrow(node)) {
10347                    for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
10348                } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
10349                    safe = false;
10350                }
10351                return true;
10352            }
10353            if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
10354        });
10355        fn.walk(tw);
10356        return safe;
10357    }
10358
10359    function safe_from_strict_mode(fn, compressor) {
10360        return fn.in_strict_mode(compressor) || !compressor.has_directive("use strict");
10361    }
10362
10363    OPT(AST_Call, function(self, compressor) {
10364        var exp = self.expression;
10365        var terminated = trim_optional_chain(self, compressor);
10366        if (terminated) return terminated;
10367        if (compressor.option("sequences")) {
10368            if (exp instanceof AST_PropAccess) {
10369                var seq = lift_sequence_in_expression(exp, compressor);
10370                if (seq !== exp) {
10371                    var call = self.clone();
10372                    call.expression = seq.expressions.pop();
10373                    seq.expressions.push(call);
10374                    return seq.optimize(compressor);
10375                }
10376            } else if (!needs_unbinding(exp.tail_node())) {
10377                var seq = lift_sequence_in_expression(self, compressor);
10378                if (seq !== self) return seq.optimize(compressor);
10379            }
10380        }
10381        if (compressor.option("unused")) drop_unused_call_args(self, compressor);
10382        if (compressor.option("unsafe")) {
10383            if (is_undeclared_ref(exp)) switch (exp.name) {
10384              case "Array":
10385                // Array(n) ---> [ , , ... , ]
10386                if (self.args.length == 1) {
10387                    var first = self.args[0];
10388                    if (first instanceof AST_Number) try {
10389                        var length = first.value;
10390                        if (length > 6) break;
10391                        var elements = Array(length);
10392                        for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
10393                        return make_node(AST_Array, self, { elements: elements });
10394                    } catch (ex) {
10395                        AST_Node.warn("Invalid array length: {length} [{start}]", {
10396                            length: length,
10397                            start: self.start,
10398                        });
10399                        break;
10400                    }
10401                    if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
10402                }
10403                // Array(...) ---> [ ... ]
10404                return make_node(AST_Array, self, { elements: self.args });
10405              case "Object":
10406                // Object() ---> {}
10407                if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
10408                break;
10409              case "String":
10410                // String() ---> ""
10411                if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
10412                // String(x) ---> "" + x
10413                if (self.args.length == 1) return make_node(AST_Binary, self, {
10414                    operator: "+",
10415                    left: make_node(AST_String, self, { value: "" }),
10416                    right: self.args[0],
10417                }).optimize(compressor);
10418                break;
10419              case "Number":
10420                // Number() ---> 0
10421                if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
10422                // Number(x) ---> +("" + x)
10423                if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
10424                    operator: "+",
10425                    expression: make_node(AST_Binary, self, {
10426                        operator: "+",
10427                        left: make_node(AST_String, self, { value: "" }),
10428                        right: self.args[0],
10429                    }),
10430                }).optimize(compressor);
10431                break;
10432              case "Boolean":
10433                // Boolean() ---> false
10434                if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
10435                // Boolean(x) ---> !!x
10436                if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
10437                    operator: "!",
10438                    expression: make_node(AST_UnaryPrefix, self, {
10439                        operator: "!",
10440                        expression: self.args[0],
10441                    }),
10442                }).optimize(compressor);
10443                break;
10444              case "RegExp":
10445                // attempt to convert RegExp(...) to literal
10446                var params = [];
10447                if (all(self.args, function(arg) {
10448                    var value = arg.evaluate(compressor);
10449                    params.unshift(value);
10450                    return arg !== value;
10451                })) try {
10452                    return best_of(compressor, self, make_node(AST_RegExp, self, {
10453                        value: RegExp.apply(RegExp, params),
10454                    }));
10455                } catch (ex) {
10456                    AST_Node.warn("Error converting {this} [{start}]", self);
10457                }
10458                break;
10459            } else if (exp instanceof AST_Dot) switch (exp.property) {
10460              case "toString":
10461                // x.toString() ---> "" + x
10462                var expr = exp.expression;
10463                if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
10464                    return make_node(AST_Binary, self, {
10465                        operator: "+",
10466                        left: make_node(AST_String, self, { value: "" }),
10467                        right: expr,
10468                    }).optimize(compressor);
10469                }
10470                break;
10471              case "join":
10472                if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
10473                    var separator = self.args[0];
10474                    // [].join() ---> ""
10475                    // [].join(x) ---> (x, "")
10476                    if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
10477                        return separator ? make_sequence(self, [
10478                            separator,
10479                            make_node(AST_String, self, { value: "" }),
10480                        ]).optimize(compressor) : make_node(AST_String, self, { value: "" });
10481                    }
10482                    if (separator) {
10483                        separator = separator.evaluate(compressor);
10484                        if (separator instanceof AST_Node) break EXIT; // not a constant
10485                    }
10486                    var elements = [];
10487                    var consts = [];
10488                    for (var i = 0; i < exp.expression.elements.length; i++) {
10489                        var el = exp.expression.elements[i];
10490                        var value = el.evaluate(compressor);
10491                        if (value !== el) {
10492                            consts.push(value);
10493                        } else if (el instanceof AST_Spread) {
10494                            break EXIT;
10495                        } else {
10496                            if (consts.length > 0) {
10497                                elements.push(make_node(AST_String, self, { value: consts.join(separator) }));
10498                                consts.length = 0;
10499                            }
10500                            elements.push(el);
10501                        }
10502                    }
10503                    if (consts.length > 0) elements.push(make_node(AST_String, self, {
10504                        value: consts.join(separator),
10505                    }));
10506                    // [ x ].join() ---> "" + x
10507                    // [ x ].join(".") ---> "" + x
10508                    // [ 1, 2, 3 ].join() ---> "1,2,3"
10509                    // [ 1, 2, 3 ].join(".") ---> "1.2.3"
10510                    if (elements.length == 1) {
10511                        if (elements[0].is_string(compressor)) return elements[0];
10512                        return make_node(AST_Binary, elements[0], {
10513                            operator: "+",
10514                            left: make_node(AST_String, self, { value: "" }),
10515                            right: elements[0],
10516                        });
10517                    }
10518                    // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
10519                    if (separator == "") {
10520                        var first;
10521                        if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
10522                            first = elements.shift();
10523                        } else {
10524                            first = make_node(AST_String, self, { value: "" });
10525                        }
10526                        return elements.reduce(function(prev, el) {
10527                            return make_node(AST_Binary, el, {
10528                                operator: "+",
10529                                left: prev,
10530                                right: el,
10531                            });
10532                        }, first).optimize(compressor);
10533                    }
10534                    // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
10535                    // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
10536                    // need this awkward cloning to not affect original element
10537                    // best_of will decide which one to get through.
10538                    var node = self.clone();
10539                    node.expression = node.expression.clone();
10540                    node.expression.expression = node.expression.expression.clone();
10541                    node.expression.expression.elements = elements;
10542                    return best_of(compressor, self, node);
10543                }
10544                break;
10545              case "charAt":
10546                if (self.args.length < 2) {
10547                    var node = make_node(AST_Binary, self, {
10548                        operator: "||",
10549                        left: make_node(AST_Sub, self, {
10550                            expression: exp.expression,
10551                            property: self.args.length ? make_node(AST_Binary, self.args[0], {
10552                                operator: "|",
10553                                left: make_node(AST_Number, self, { value: 0 }),
10554                                right: self.args[0],
10555                            }) : make_node(AST_Number, self, { value: 0 }),
10556                        }).optimize(compressor),
10557                        right: make_node(AST_String, self, { value: "" }),
10558                    });
10559                    node.is_string = return_true;
10560                    return node.optimize(compressor);
10561                }
10562                break;
10563              case "apply":
10564                if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
10565                    var args = self.args[1].elements.slice();
10566                    args.unshift(self.args[0]);
10567                    return make_node(AST_Call, self, {
10568                        expression: make_node(AST_Dot, exp, {
10569                            expression: exp.expression,
10570                            property: "call",
10571                        }),
10572                        args: args,
10573                    }).optimize(compressor);
10574                }
10575                break;
10576              case "call":
10577                var func = exp.expression;
10578                if (func instanceof AST_SymbolRef) {
10579                    func = func.fixed_value();
10580                }
10581                if (func instanceof AST_Lambda && !func.contains_this()) {
10582                    return (self.args.length ? make_sequence(self, [
10583                        self.args[0],
10584                        make_node(AST_Call, self, {
10585                            expression: exp.expression,
10586                            args: self.args.slice(1),
10587                        }),
10588                    ]) : make_node(AST_Call, self, {
10589                        expression: exp.expression,
10590                        args: [],
10591                    })).optimize(compressor);
10592                }
10593                break;
10594            } else if (compressor.option("side_effects")
10595                && exp instanceof AST_Call
10596                && exp.args.length == 1
10597                && is_undeclared_ref(exp.expression)
10598                && exp.expression.name == "Object") {
10599                var call = self.clone();
10600                call.expression = maintain_this_binding(self, exp, exp.args[0]);
10601                return call.optimize(compressor);
10602            }
10603        }
10604        if (compressor.option("unsafe_Function")
10605            && is_undeclared_ref(exp)
10606            && exp.name == "Function") {
10607            // new Function() ---> function(){}
10608            if (self.args.length == 0) return make_node(AST_Function, self, {
10609                argnames: [],
10610                body: [],
10611            }).init_vars(exp.scope);
10612            if (all(self.args, function(x) {
10613                return x instanceof AST_String;
10614            })) {
10615                // quite a corner-case, but we can handle it:
10616                //   https://github.com/mishoo/UglifyJS/issues/203
10617                // if the code argument is a constant, then we can minify it.
10618                try {
10619                    var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
10620                        return arg.value;
10621                    }).join() + "){" + self.args[self.args.length - 1].value + "})";
10622                    var ast = parse(code);
10623                    var mangle = { ie: compressor.option("ie") };
10624                    ast.figure_out_scope(mangle);
10625                    var comp = new Compressor(compressor.options);
10626                    ast = ast.transform(comp);
10627                    ast.figure_out_scope(mangle);
10628                    ast.compute_char_frequency(mangle);
10629                    ast.mangle_names(mangle);
10630                    var fun;
10631                    ast.walk(new TreeWalker(function(node) {
10632                        if (fun) return true;
10633                        if (node instanceof AST_Lambda) {
10634                            fun = node;
10635                            return true;
10636                        }
10637                    }));
10638                    var code = OutputStream();
10639                    AST_BlockStatement.prototype._codegen.call(fun, code);
10640                    self.args = [
10641                        make_node(AST_String, self, {
10642                            value: fun.argnames.map(function(arg) {
10643                                return arg.print_to_string();
10644                            }).join(),
10645                        }),
10646                        make_node(AST_String, self.args[self.args.length - 1], {
10647                            value: code.get().replace(/^\{|\}$/g, "")
10648                        }),
10649                    ];
10650                    return self;
10651                } catch (ex) {
10652                    if (ex instanceof JS_Parse_Error) {
10653                        AST_Node.warn("Error parsing code passed to new Function [{start}]", self.args[self.args.length - 1]);
10654                        AST_Node.warn(ex.toString());
10655                    } else {
10656                        throw ex;
10657                    }
10658                }
10659            }
10660        }
10661        var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
10662        var parent = compressor.parent(), current = compressor.self();
10663        var is_func = fn instanceof AST_Lambda
10664            && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
10665            && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
10666        var stat = is_func && fn.first_statement();
10667        var has_default = 0, has_destructured = false;
10668        var has_spread = !all(self.args, function(arg) {
10669            return !(arg instanceof AST_Spread);
10670        });
10671        var can_drop = is_func && all(fn.argnames, function(argname, index) {
10672            if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
10673            if (argname instanceof AST_DefaultValue) {
10674                if (!has_default) has_default = 1;
10675                var arg = has_default == 1 && self.args[index];
10676                if (!is_undefined(arg)) has_default = 2;
10677                if (has_arg_refs(fn, argname.value)) return false;
10678                argname = argname.name;
10679            }
10680            if (argname instanceof AST_Destructured) {
10681                has_destructured = true;
10682                if (has_arg_refs(fn, argname)) return false;
10683            }
10684            return true;
10685        }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn, fn.rest));
10686        var can_inline = can_drop
10687            && compressor.option("inline")
10688            && !self.is_expr_pure(compressor)
10689            && (exp === fn || safe_from_strict_mode(fn, compressor));
10690        if (can_inline && stat instanceof AST_Return) {
10691            var value = stat.value;
10692            if (exp === fn
10693                && !fn.name
10694                && (!value || value.is_constant_expression())
10695                && safe_from_await_yield(fn, avoid_await_yield(compressor))) {
10696                return make_sequence(self, convert_args(value)).optimize(compressor);
10697            }
10698        }
10699        if (is_func && !fn.contains_this()) {
10700            var def, value, var_assigned = false;
10701            if (can_inline
10702                && !fn.uses_arguments
10703                && !fn.pinned()
10704                && !(fn.name && fn instanceof AST_LambdaExpression)
10705                && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
10706                    && fn.is_constant_expression(find_scope(compressor)))
10707                && (value = can_flatten_body(stat))) {
10708                var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
10709                if (can_substitute_directly()) {
10710                    var args = self.args.slice();
10711                    var refs = [];
10712                    var retValue = value.clone(true).transform(new TreeTransformer(function(node) {
10713                        if (node instanceof AST_SymbolRef) {
10714                            var def = node.definition();
10715                            if (fn.variables.get(node.name) !== def) {
10716                                refs.push(node);
10717                                return node;
10718                            }
10719                            var index = resolve_index(def);
10720                            var arg = args[index];
10721                            if (!arg) return make_node(AST_Undefined, self);
10722                            args[index] = null;
10723                            var parent = this.parent();
10724                            return parent ? maintain_this_binding(parent, node, arg) : arg;
10725                        }
10726                    }));
10727                    var save_inlined = fn.inlined;
10728                    if (exp !== fn) fn.inlined = true;
10729                    var exprs = [];
10730                    args.forEach(function(arg) {
10731                        if (!arg) return;
10732                        arg = arg.clone(true);
10733                        arg.walk(new TreeWalker(function(node) {
10734                            if (node instanceof AST_SymbolRef) refs.push(node);
10735                        }));
10736                        exprs.push(arg);
10737                    }, []);
10738                    exprs.push(retValue);
10739                    var node = make_sequence(self, exprs).optimize(compressor);
10740                    fn.inlined = save_inlined;
10741                    node = maintain_this_binding(parent, current, node);
10742                    if (replacing || best_of_expression(node, self) === node) {
10743                        refs.forEach(function(ref) {
10744                            ref.scope = exp === fn ? fn.parent_scope : exp.scope;
10745                            ref.reference();
10746                            var def = ref.definition();
10747                            if (replacing) def.replaced++;
10748                            def.single_use = false;
10749                        });
10750                        return node;
10751                    } else if (!node.has_side_effects(compressor)) {
10752                        self.drop_side_effect_free = function(compressor, first_in_statement) {
10753                            var self = this;
10754                            var exprs = self.args.slice();
10755                            exprs.unshift(self.expression);
10756                            return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement);
10757                        };
10758                    }
10759                }
10760                var arg_used, insert, in_loop, scope;
10761                if (replacing && can_inject_symbols()) {
10762                    fn._squeezed = true;
10763                    if (exp !== fn) fn.parent_scope = exp.scope;
10764                    var node = make_sequence(self, flatten_fn()).optimize(compressor);
10765                    return maintain_this_binding(parent, current, node);
10766                }
10767            }
10768            if (compressor.option("side_effects")
10769                && can_drop
10770                && all(fn.body, is_empty)
10771                && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
10772                && !(is_arrow(fn) && fn.value)
10773                && safe_from_await_yield(fn, avoid_await_yield(compressor))) {
10774                return make_sequence(self, convert_args()).optimize(compressor);
10775            }
10776        }
10777        if (compressor.option("drop_console")) {
10778            if (exp instanceof AST_PropAccess) {
10779                var name = exp.expression;
10780                while (name.expression) {
10781                    name = name.expression;
10782                }
10783                if (is_undeclared_ref(name) && name.name == "console") {
10784                    return make_node(AST_Undefined, self).optimize(compressor);
10785                }
10786            }
10787        }
10788        if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
10789            return self.negate(compressor, true);
10790        }
10791        return try_evaluate(compressor, self);
10792
10793        function make_void_lhs(orig) {
10794            return make_node(AST_Sub, orig, {
10795                expression: make_node(AST_Array, orig, { elements: [] }),
10796                property: make_node(AST_Number, orig, { value: 0 }),
10797            });
10798        }
10799
10800        function convert_args(value) {
10801            var args = self.args.slice();
10802            var destructured = has_default > 1 || has_destructured || fn.rest;
10803            if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
10804            if (destructured) {
10805                var tt = new TreeTransformer(function(node, descend) {
10806                    if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
10807                        name: node.name.transform(tt) || make_void_lhs(node),
10808                        value: node.value,
10809                    });
10810                    if (node instanceof AST_DestructuredArray) {
10811                        var elements = [];
10812                        node.elements.forEach(function(node, index) {
10813                            node = node.transform(tt);
10814                            if (node) elements[index] = node;
10815                        });
10816                        fill_holes(node, elements);
10817                        return make_node(AST_DestructuredArray, node, { elements: elements });
10818                    }
10819                    if (node instanceof AST_DestructuredObject) {
10820                        var properties = [], side_effects = [];
10821                        node.properties.forEach(function(prop) {
10822                            var key = prop.key;
10823                            var value = prop.value.transform(tt);
10824                            if (value) {
10825                                if (side_effects.length) {
10826                                    if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop);
10827                                    side_effects.push(key);
10828                                    key = make_sequence(node, side_effects);
10829                                    side_effects = [];
10830                                }
10831                                properties.push(make_node(AST_DestructuredKeyVal, prop, {
10832                                    key: key,
10833                                    value: value,
10834                                }));
10835                            } else if (key instanceof AST_Node) {
10836                                side_effects.push(key);
10837                            }
10838                        });
10839                        if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
10840                            key: make_sequence(node, side_effects),
10841                            value: make_void_lhs(node),
10842                        }));
10843                        return make_node(AST_DestructuredObject, node, { properties: properties });
10844                    }
10845                    if (node instanceof AST_SymbolFunarg) return null;
10846                });
10847                var lhs = [];
10848                fn.argnames.forEach(function(argname, index) {
10849                    argname = argname.transform(tt);
10850                    if (argname) lhs[index] = argname;
10851                });
10852                var rest = fn.rest && fn.rest.transform(tt);
10853                if (rest) lhs.length = fn.argnames.length;
10854                fill_holes(fn, lhs);
10855                args[0] = make_node(AST_Assign, self, {
10856                    operator: "=",
10857                    left: make_node(AST_DestructuredArray, fn, {
10858                        elements: lhs,
10859                        rest: rest,
10860                    }),
10861                    right: args[0],
10862                });
10863            } else fn.argnames.forEach(function(argname) {
10864                if (argname instanceof AST_DefaultValue) args.push(argname.value);
10865            });
10866            args.push(value || make_node(AST_Undefined, self));
10867            return args;
10868        }
10869
10870        function noop_value() {
10871            return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
10872        }
10873
10874        function return_value(stat) {
10875            if (!stat) return noop_value();
10876            if (stat instanceof AST_Return) return stat.value || noop_value();
10877            if (stat instanceof AST_SimpleStatement) {
10878                return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, {
10879                    operator: "void",
10880                    expression: stat.body,
10881                });
10882            }
10883        }
10884
10885        function can_flatten_body(stat) {
10886            var len = fn.body.length;
10887            if (len < 2) {
10888                stat = return_value(stat);
10889                if (stat) return stat;
10890            }
10891            if (compressor.option("inline") < 3) return false;
10892            stat = null;
10893            for (var i = 0; i < len; i++) {
10894                var line = fn.body[i];
10895                if (line instanceof AST_Var) {
10896                    if (var_assigned) {
10897                        if (!stat) continue;
10898                        if (!(stat instanceof AST_SimpleStatement)) return false;
10899                        if (!declarations_only(line)) stat = null;
10900                    } else if (!declarations_only(line)) {
10901                        if (stat && !(stat instanceof AST_SimpleStatement)) return false;
10902                        stat = null;
10903                        var_assigned = true;
10904                    }
10905                } else if (line instanceof AST_AsyncDefun
10906                    || line instanceof AST_Defun
10907                    || line instanceof AST_EmptyStatement) {
10908                    continue;
10909                } else if (stat) {
10910                    return false;
10911                } else {
10912                    stat = line;
10913                }
10914            }
10915            return return_value(stat);
10916        }
10917
10918        function resolve_index(def) {
10919            for (var i = fn.argnames.length; --i >= 0;) {
10920                if (fn.argnames[i].definition() === def) return i;
10921            }
10922        }
10923
10924        function can_substitute_directly() {
10925            if (has_default || has_destructured || has_spread || var_assigned || fn.rest) return;
10926            if (compressor.option("inline") < 2 && fn.argnames.length) return;
10927            if (!fn.variables.all(function(def) {
10928                return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
10929            })) return;
10930            var scope = compressor.find_parent(AST_Scope);
10931            var abort = false;
10932            var avoid = avoid_await_yield(compressor, scope);
10933            var begin;
10934            var in_order = [];
10935            var side_effects = false;
10936            var tw = new TreeWalker(function(node, descend) {
10937                if (abort) return true;
10938                if (node instanceof AST_Binary && lazy_op[node.operator]
10939                    || node instanceof AST_Conditional) {
10940                    in_order = null;
10941                    return;
10942                }
10943                if (node instanceof AST_Scope) return abort = true;
10944                if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
10945                if (node instanceof AST_SymbolRef) {
10946                    var def = node.definition();
10947                    if (fn.variables.get(node.name) !== def) {
10948                        in_order = null;
10949                        return;
10950                    }
10951                    if (def.init instanceof AST_LambdaDefinition) return abort = true;
10952                    if (is_lhs(node, tw.parent())) return abort = true;
10953                    var index = resolve_index(def);
10954                    if (!(begin < index)) begin = index;
10955                    if (!in_order) return;
10956                    if (side_effects) {
10957                        in_order = null;
10958                    } else {
10959                        in_order.push(fn.argnames[index]);
10960                    }
10961                    return;
10962                }
10963                if (side_effects) return;
10964                if (node instanceof AST_Assign && node.left instanceof AST_PropAccess) {
10965                    node.left.expression.walk(tw);
10966                    if (node.left instanceof AST_Sub) node.left.property.walk(tw);
10967                    node.right.walk(tw);
10968                    side_effects = true;
10969                    return true;
10970                }
10971                if (node.has_side_effects(compressor)) {
10972                    descend();
10973                    side_effects = true;
10974                    return true;
10975                }
10976            });
10977            value.walk(tw);
10978            if (abort) return;
10979            var end = self.args.length;
10980            if (in_order && fn.argnames.length >= end) {
10981                end = fn.argnames.length;
10982                while (end-- > begin && fn.argnames[end] === in_order.pop());
10983                end++;
10984            }
10985            return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) {
10986                return funarg.is_constant_expression(scope);
10987            } : function(funarg) {
10988                return !funarg.has_side_effects(compressor);
10989            });
10990        }
10991
10992        function var_exists(defined, name) {
10993            return defined.has(name) || identifier_atom[name] || scope.var_names().has(name);
10994        }
10995
10996        function can_inject_args(defined, safe_to_inject) {
10997            var abort = false;
10998            fn.each_argname(function(arg) {
10999                if (abort) return;
11000                if (arg.unused) return;
11001                if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
11002                arg_used.set(arg.name, true);
11003                if (in_loop) in_loop.push(arg.definition());
11004            });
11005            return !abort;
11006        }
11007
11008        function can_inject_vars(defined, safe_to_inject) {
11009            for (var i = 0; i < fn.body.length; i++) {
11010                var stat = fn.body[i];
11011                if (stat instanceof AST_LambdaDefinition) {
11012                    var name = stat.name;
11013                    if (!safe_to_inject) return false;
11014                    if (arg_used.has(name.name)) return false;
11015                    if (var_exists(defined, name.name)) return false;
11016                    if (!all(stat.enclosed, function(def) {
11017                        return def.scope === scope || def.scope === stat || !defined.has(def.name);
11018                    })) return false;
11019                    if (in_loop) in_loop.push(name.definition());
11020                    continue;
11021                }
11022                if (!(stat instanceof AST_Var)) continue;
11023                if (!safe_to_inject) return false;
11024                for (var j = stat.definitions.length; --j >= 0;) {
11025                    var name = stat.definitions[j].name;
11026                    if (var_exists(defined, name.name)) return false;
11027                    if (in_loop) in_loop.push(name.definition());
11028                }
11029            }
11030            return true;
11031        }
11032
11033        function can_inject_symbols() {
11034            var defined = new Dictionary();
11035            var level = 0, child;
11036            scope = current;
11037            do {
11038                if (scope.variables) scope.variables.each(function(def) {
11039                    defined.set(def.name, true);
11040                });
11041                child = scope;
11042                scope = compressor.parent(level++);
11043                if (scope instanceof AST_ClassField) {
11044                    if (!scope.static) return false;
11045                } else if (scope instanceof AST_DWLoop) {
11046                    in_loop = [];
11047                } else if (scope instanceof AST_For) {
11048                    if (scope.init === child) continue;
11049                    in_loop = [];
11050                } else if (scope instanceof AST_ForEnumeration) {
11051                    if (scope.init === child) continue;
11052                    if (scope.object === child) continue;
11053                    in_loop = [];
11054                }
11055            } while (!(scope instanceof AST_Scope));
11056            insert = scope.body.indexOf(child) + 1;
11057            if (!insert) return false;
11058            if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return false;
11059            var safe_to_inject = (exp !== fn || fn.parent_scope.resolve() === scope) && !scope.pinned();
11060            if (scope instanceof AST_Toplevel) {
11061                if (compressor.toplevel.vars) {
11062                    defined.set("arguments", true);
11063                } else {
11064                    safe_to_inject = false;
11065                }
11066            }
11067            arg_used = new Dictionary();
11068            var inline = compressor.option("inline");
11069            if (!can_inject_args(defined, inline >= 2 && safe_to_inject)) return false;
11070            if (!can_inject_vars(defined, inline >= 3 && safe_to_inject)) return false;
11071            return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
11072        }
11073
11074        function append_var(decls, expressions, name, value) {
11075            var def = name.definition();
11076            if (!scope.var_names().has(name.name)) {
11077                scope.var_names().set(name.name, true);
11078                decls.push(make_node(AST_VarDef, name, {
11079                    name: name,
11080                    value: null,
11081                }));
11082            }
11083            scope.variables.set(name.name, def);
11084            scope.enclosed.push(def);
11085            if (!value) return;
11086            var sym = make_node(AST_SymbolRef, name);
11087            def.assignments++;
11088            def.references.push(sym);
11089            expressions.push(make_node(AST_Assign, self, {
11090                operator: "=",
11091                left: sym,
11092                right: value,
11093            }));
11094        }
11095
11096        function flatten_args(decls, expressions) {
11097            var len = fn.argnames.length;
11098            for (var i = self.args.length; --i >= len;) {
11099                expressions.push(self.args[i]);
11100            }
11101            var default_args = [];
11102            for (i = len; --i >= 0;) {
11103                var argname = fn.argnames[i];
11104                var name;
11105                if (argname instanceof AST_DefaultValue) {
11106                    default_args.push(argname);
11107                    name = argname.name;
11108                } else {
11109                    name = argname;
11110                }
11111                var value = self.args[i];
11112                if (name.unused || scope.var_names().has(name.name)) {
11113                    if (value) expressions.push(value);
11114                } else {
11115                    var symbol = make_node(AST_SymbolVar, name);
11116                    var def = name.definition();
11117                    def.orig.push(symbol);
11118                    def.eliminated++;
11119                    if (name.unused !== undefined) {
11120                        append_var(decls, expressions, symbol);
11121                        if (value) expressions.push(value);
11122                    } else {
11123                        if (!value && argname === name && (in_loop
11124                            || name.name == "arguments" && !is_arrow(fn) && is_arrow(scope))) {
11125                            value = make_node(AST_Undefined, self);
11126                        }
11127                        append_var(decls, expressions, symbol, value);
11128                    }
11129                }
11130            }
11131            decls.reverse();
11132            expressions.reverse();
11133            for (i = default_args.length; --i >= 0;) {
11134                var node = default_args[i];
11135                if (node.name.unused !== undefined) {
11136                    expressions.push(node.value);
11137                } else {
11138                    var sym = make_node(AST_SymbolRef, node.name);
11139                    node.name.definition().references.push(sym);
11140                    expressions.push(make_node(AST_Assign, node, {
11141                        operator: "=",
11142                        left: sym,
11143                        right: node.value,
11144                    }));
11145                }
11146            }
11147        }
11148
11149        function flatten_destructured(decls, expressions) {
11150            expressions.push(make_node(AST_Assign, self, {
11151                operator: "=",
11152                left: make_node(AST_DestructuredArray, self, {
11153                    elements: fn.argnames.map(function(argname) {
11154                        if (argname.unused) return make_node(AST_Hole, argname);
11155                        return argname.convert_symbol(AST_SymbolRef, process);
11156                    }),
11157                    rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
11158                }),
11159                right: make_node(AST_Array, self, { elements: self.args.slice() }),
11160            }));
11161
11162            function process(ref, name) {
11163                if (name.unused) return make_void_lhs(name);
11164                var def = name.definition();
11165                def.assignments++;
11166                def.references.push(ref);
11167                var symbol = make_node(AST_SymbolVar, name);
11168                def.orig.push(symbol);
11169                def.eliminated++;
11170                append_var(decls, expressions, symbol);
11171            }
11172        }
11173
11174        function flatten_vars(decls, expressions) {
11175            var args = [ insert, 0 ];
11176            var decl_var = [], expr_fn = [], expr_var = [], expr_loop = [], exprs = [];
11177            fn.body.filter(in_loop ? function(stat) {
11178                if (!(stat instanceof AST_LambdaDefinition)) return true;
11179                var name = make_node(AST_SymbolVar, flatten_var(stat.name));
11180                var def = name.definition();
11181                def.fixed = false;
11182                def.orig.push(name);
11183                def.eliminated++;
11184                append_var(decls, expr_fn, name, to_func_expr(stat, true));
11185                return false;
11186            } : function(stat) {
11187                if (!(stat instanceof AST_LambdaDefinition)) return true;
11188                var def = stat.name.definition();
11189                scope.functions.set(def.name, def);
11190                scope.variables.set(def.name, def);
11191                scope.enclosed.push(def);
11192                scope.var_names().set(def.name, true);
11193                args.push(stat);
11194                return false;
11195            }).forEach(function(stat) {
11196                if (!(stat instanceof AST_Var)) {
11197                    if (stat instanceof AST_SimpleStatement) exprs.push(stat.body);
11198                    return;
11199                }
11200                for (var j = 0; j < stat.definitions.length; j++) {
11201                    var var_def = stat.definitions[j];
11202                    var name = flatten_var(var_def.name);
11203                    var value = var_def.value;
11204                    if (value && exprs.length > 0) {
11205                        exprs.push(value);
11206                        value = make_sequence(var_def, exprs);
11207                        exprs = [];
11208                    }
11209                    append_var(decl_var, expr_var, name, value);
11210                    if (!in_loop) continue;
11211                    if (arg_used.has(name.name)) continue;
11212                    if (name.definition().orig.length == 1 && fn.functions.has(name.name)) continue;
11213                    expr_loop.push(init_ref(compressor, name));
11214                }
11215            });
11216            [].push.apply(decls, decl_var);
11217            [].push.apply(expressions, expr_loop);
11218            [].push.apply(expressions, expr_fn);
11219            [].push.apply(expressions, expr_var);
11220            return args;
11221        }
11222
11223        function flatten_fn() {
11224            var decls = [];
11225            var expressions = [];
11226            if (has_default > 1 || has_destructured || has_spread || fn.rest) {
11227                flatten_destructured(decls, expressions);
11228            } else {
11229                flatten_args(decls, expressions);
11230            }
11231            var args = flatten_vars(decls, expressions);
11232            expressions.push(value);
11233            if (decls.length) args.push(make_node(AST_Var, fn, { definitions: decls }));
11234            [].splice.apply(scope.body, args);
11235            fn.enclosed.forEach(function(def) {
11236                if (scope.var_names().has(def.name)) return;
11237                scope.enclosed.push(def);
11238                scope.var_names().set(def.name, true);
11239            });
11240            return expressions;
11241        }
11242    });
11243
11244    OPT(AST_New, function(self, compressor) {
11245        if (compressor.option("unsafe")) {
11246            var exp = self.expression;
11247            if (is_undeclared_ref(exp)) switch (exp.name) {
11248              case "Array":
11249              case "Error":
11250              case "Function":
11251              case "Object":
11252              case "RegExp":
11253                return make_node(AST_Call, self).transform(compressor);
11254            }
11255        }
11256        if (compressor.option("sequences")) {
11257            var seq = lift_sequence_in_expression(self, compressor);
11258            if (seq !== self) return seq.optimize(compressor);
11259        }
11260        if (compressor.option("unused")) drop_unused_call_args(self, compressor);
11261        return self;
11262    });
11263
11264    // (a = b, x && a = c) ---> a = x ? c : b
11265    // (a = b, x || a = c) ---> a = x ? b : c
11266    function to_conditional_assignment(compressor, def, value, node) {
11267        if (!(node instanceof AST_Binary)) return;
11268        if (!(node.operator == "&&" || node.operator == "||")) return;
11269        if (!(node.right instanceof AST_Assign)) return;
11270        if (node.right.operator != "=") return;
11271        if (!(node.right.left instanceof AST_SymbolRef)) return;
11272        if (node.right.left.definition() !== def) return;
11273        if (value.has_side_effects(compressor)) return;
11274        if (!safe_from_assignment(node.left)) return;
11275        if (!safe_from_assignment(node.right.right)) return;
11276        def.replaced++;
11277        return node.operator == "&&" ? make_node(AST_Conditional, node, {
11278            condition: node.left,
11279            consequent: node.right.right,
11280            alternative: value,
11281        }) : make_node(AST_Conditional, node, {
11282            condition: node.left,
11283            consequent: value,
11284            alternative: node.right.right,
11285        });
11286
11287        function safe_from_assignment(node) {
11288            if (node.has_side_effects(compressor)) return;
11289            var hit = false;
11290            node.walk(new TreeWalker(function(node) {
11291                if (hit) return true;
11292                if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
11293            }));
11294            return !hit;
11295        }
11296    }
11297
11298    OPT(AST_Sequence, function(self, compressor) {
11299        var expressions = filter_for_side_effects();
11300        var end = expressions.length - 1;
11301        merge_assignments();
11302        trim_right_for_undefined();
11303        if (end == 0) {
11304            self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]);
11305            if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
11306            return self;
11307        }
11308        self.expressions = expressions;
11309        return self;
11310
11311        function filter_for_side_effects() {
11312            if (!compressor.option("side_effects")) return self.expressions;
11313            var expressions = [];
11314            var first = first_in_statement(compressor);
11315            var last = self.expressions.length - 1;
11316            self.expressions.forEach(function(expr, index) {
11317                if (index < last) expr = expr.drop_side_effect_free(compressor, first);
11318                if (expr) {
11319                    merge_sequence(expressions, expr);
11320                    first = false;
11321                }
11322            });
11323            return expressions;
11324        }
11325
11326        function trim_right_for_undefined() {
11327            if (!compressor.option("side_effects")) return;
11328            while (end > 0 && is_undefined(expressions[end], compressor)) end--;
11329            if (end < expressions.length - 1) {
11330                expressions[end] = make_node(AST_UnaryPrefix, self, {
11331                    operator: "void",
11332                    expression: expressions[end],
11333                });
11334                expressions.length = end + 1;
11335            }
11336        }
11337
11338        function is_simple_assign(node) {
11339            return node instanceof AST_Assign
11340                && node.operator == "="
11341                && node.left instanceof AST_SymbolRef
11342                && node.left.definition();
11343        }
11344
11345        function merge_assignments() {
11346            for (var i = 1; i < end; i++) {
11347                var prev = expressions[i - 1];
11348                var def = is_simple_assign(prev);
11349                if (!def) continue;
11350                var expr = expressions[i];
11351                if (compressor.option("conditionals")) {
11352                    var cond = to_conditional_assignment(compressor, def, prev.right, expr);
11353                    if (cond) {
11354                        prev.right = cond;
11355                        expressions.splice(i--, 1);
11356                        end--;
11357                        continue;
11358                    }
11359                }
11360                if (compressor.option("dead_code")
11361                    && is_simple_assign(expr) === def
11362                    && expr.right.is_constant_expression(def.scope.resolve())) {
11363                    expressions[--i] = prev.right;
11364                }
11365            }
11366        }
11367    });
11368
11369    OPT(AST_UnaryPostfix, function(self, compressor) {
11370        if (compressor.option("sequences")) {
11371            var seq = lift_sequence_in_expression(self, compressor);
11372            if (seq !== self) return seq.optimize(compressor);
11373        }
11374        return try_evaluate(compressor, self);
11375    });
11376
11377    var SIGN_OPS = makePredicate("+ -");
11378    var MULTIPLICATIVE_OPS = makePredicate("* / %");
11379    OPT(AST_UnaryPrefix, function(self, compressor) {
11380        var op = self.operator;
11381        var exp = self.expression;
11382        if (compressor.option("sequences") && can_lift()) {
11383            var seq = lift_sequence_in_expression(self, compressor);
11384            if (seq !== self) return seq.optimize(compressor);
11385        }
11386        switch (op) {
11387          case "+":
11388            if (!compressor.option("evaluate")) break;
11389            if (!exp.is_number(compressor, true)) break;
11390            var parent = compressor.parent();
11391            if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") break;
11392            return exp;
11393          case "-":
11394            if (exp instanceof AST_Infinity) exp = exp.transform(compressor);
11395            // avoids infinite recursion of numerals
11396            if (exp instanceof AST_Number || exp instanceof AST_Infinity) return self;
11397            break;
11398          case "!":
11399            if (!compressor.option("booleans")) break;
11400            if (exp.is_truthy()) return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
11401            if (compressor.in_boolean_context()) {
11402                // !!foo ---> foo, if we're in boolean context
11403                if (exp instanceof AST_UnaryPrefix && exp.operator == "!") return exp.expression;
11404                if (exp instanceof AST_Binary) {
11405                    var first = first_in_statement(compressor);
11406                    self = (first ? best_of_statement : best_of_expression)(self, exp.negate(compressor, first));
11407                }
11408            }
11409            break;
11410          case "delete":
11411            if (!compressor.option("evaluate")) break;
11412            if (may_not_delete(exp)) break;
11413            return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
11414          case "typeof":
11415            if (!compressor.option("booleans")) break;
11416            if (!compressor.in_boolean_context()) break;
11417            // typeof always returns a non-empty string, thus always truthy
11418            AST_Node.warn("Boolean expression always true [{start}]", self);
11419            var exprs = [ make_node(AST_True, self) ];
11420            if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
11421            return make_sequence(self, exprs).optimize(compressor);
11422          case "void":
11423            if (!compressor.option("side_effects")) break;
11424            exp = exp.drop_side_effect_free(compressor);
11425            if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
11426            self.expression = exp;
11427            return self;
11428        }
11429        if (compressor.option("evaluate")
11430            && exp instanceof AST_Binary
11431            && SIGN_OPS[op]
11432            && MULTIPLICATIVE_OPS[exp.operator]
11433            && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
11434            return make_node(AST_Binary, self, {
11435                operator: exp.operator,
11436                left: make_node(AST_UnaryPrefix, exp.left, {
11437                    operator: op,
11438                    expression: exp.left,
11439                }),
11440                right: exp.right,
11441            });
11442        }
11443        return try_evaluate(compressor, self);
11444
11445        function may_not_delete(node) {
11446            return node instanceof AST_Infinity
11447                || node instanceof AST_NaN
11448                || node instanceof AST_NewTarget
11449                || node instanceof AST_PropAccess
11450                || node instanceof AST_SymbolRef
11451                || node instanceof AST_Undefined;
11452        }
11453
11454        function can_lift() {
11455            switch (op) {
11456              case "delete":
11457                return !may_not_delete(exp.tail_node());
11458              case "typeof":
11459                return !is_undeclared_ref(exp.tail_node());
11460              default:
11461                return true;
11462            }
11463        }
11464    });
11465
11466    OPT(AST_Await, function(self, compressor) {
11467        if (!compressor.option("awaits")) return self;
11468        if (compressor.option("sequences")) {
11469            var seq = lift_sequence_in_expression(self, compressor);
11470            if (seq !== self) return seq.optimize(compressor);
11471        }
11472        if (compressor.option("side_effects")) {
11473            var exp = self.expression;
11474            if (exp instanceof AST_Await) return exp.optimize(compressor);
11475            if (exp instanceof AST_UnaryPrefix && exp.expression instanceof AST_Await) return exp.optimize(compressor);
11476            for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) {
11477                if (is_arrow(parent)) {
11478                    if (parent.value === node) return exp.optimize(compressor);
11479                } else if (parent instanceof AST_Return) {
11480                    var drop = true;
11481                    do {
11482                        node = parent;
11483                        parent = compressor.parent(level++);
11484                        if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) {
11485                            drop = false;
11486                            break;
11487                        }
11488                    } while (parent && !(parent instanceof AST_Scope));
11489                    if (drop) return exp.optimize(compressor);
11490                } else if (parent instanceof AST_Sequence) {
11491                    if (parent.tail_node() === node) continue;
11492                }
11493                break;
11494            }
11495        }
11496        return self;
11497    });
11498
11499    OPT(AST_Yield, function(self, compressor) {
11500        if (!compressor.option("yields")) return self;
11501        if (compressor.option("sequences")) {
11502            var seq = lift_sequence_in_expression(self, compressor);
11503            if (seq !== self) return seq.optimize(compressor);
11504        }
11505        var exp = self.expression;
11506        if (self.nested && exp.TYPE == "Call") {
11507            var inlined = exp.clone().optimize(compressor);
11508            if (inlined.TYPE != "Call") return inlined;
11509        }
11510        return self;
11511    });
11512
11513    AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
11514        if (this.left instanceof AST_PropAccess) {
11515            if (!(this.left.expression instanceof AST_Sequence)) return this;
11516            var x = this.left.expression.expressions.slice();
11517            var e = this.clone();
11518            e.left = e.left.clone();
11519            e.left.expression = x.pop();
11520            x.push(e);
11521            return make_sequence(this, x);
11522        }
11523        if (this.left instanceof AST_Sequence) {
11524            var x = this.left.expressions.slice();
11525            var e = this.clone();
11526            e.left = x.pop();
11527            x.push(e);
11528            return make_sequence(this, x);
11529        }
11530        if (this.right instanceof AST_Sequence) {
11531            if (this.left.has_side_effects(compressor)) return this;
11532            var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
11533            var x = this.right.expressions;
11534            var last = x.length - 1;
11535            for (var i = 0; i < last; i++) {
11536                if (!assign && x[i].has_side_effects(compressor)) break;
11537            }
11538            if (i == last) {
11539                x = x.slice();
11540                var e = this.clone();
11541                e.right = x.pop();
11542                x.push(e);
11543                return make_sequence(this, x);
11544            }
11545            if (i > 0) {
11546                var e = this.clone();
11547                e.right = make_sequence(this.right, x.slice(i));
11548                x = x.slice(0, i);
11549                x.push(e);
11550                return make_sequence(this, x);
11551            }
11552        }
11553        return this;
11554    });
11555
11556    var indexFns = makePredicate("indexOf lastIndexOf");
11557    var commutativeOperators = makePredicate("== === != !== * & | ^");
11558    function is_object(node, plain) {
11559        if (node instanceof AST_Assign) return !plain && node.operator == "=" && is_object(node.right);
11560        if (node instanceof AST_New) return !plain;
11561        if (node instanceof AST_Sequence) return is_object(node.tail_node(), plain);
11562        if (node instanceof AST_SymbolRef) return !plain && is_object(node.fixed_value());
11563        return node instanceof AST_Array
11564            || node instanceof AST_Class
11565            || node instanceof AST_Lambda
11566            || node instanceof AST_Object;
11567    }
11568
11569    function can_drop_op(op, rhs, compressor) {
11570        switch (op) {
11571          case "in":
11572            return is_object(rhs) || compressor && compressor.option("unsafe_comps");
11573          case "instanceof":
11574            if (rhs instanceof AST_SymbolRef) rhs = rhs.fixed_value();
11575            return is_lambda(rhs) || compressor && compressor.option("unsafe_comps");
11576          default:
11577            return true;
11578        }
11579    }
11580
11581    function needs_enqueuing(compressor, node) {
11582        if (node.is_constant()) return true;
11583        if (node instanceof AST_Assign) return node.operator != "=" || needs_enqueuing(compressor, node.right);
11584        if (node instanceof AST_Binary) {
11585            return !lazy_op[node.operator]
11586                || needs_enqueuing(compressor, node.left) && needs_enqueuing(compressor, node.right);
11587        }
11588        if (node instanceof AST_Call) return is_async(node.expression);
11589        if (node instanceof AST_Conditional) {
11590            return needs_enqueuing(compressor, node.consequent) && needs_enqueuing(compressor, node.alternative);
11591        }
11592        if (node instanceof AST_Sequence) return needs_enqueuing(compressor, node.tail_node());
11593        if (node instanceof AST_SymbolRef) {
11594            var fixed = node.fixed_value();
11595            return fixed && needs_enqueuing(compressor, fixed);
11596        }
11597        if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag);
11598        if (node instanceof AST_Unary) return true;
11599    }
11600
11601    function extract_lhs(node, compressor) {
11602        if (node instanceof AST_Assign) return is_lhs_read_only(node.left, compressor) ? node : node.left;
11603        if (node instanceof AST_Sequence) return extract_lhs(node.tail_node(), compressor);
11604        if (node instanceof AST_UnaryPrefix && UNARY_POSTFIX[node.operator]) {
11605            return is_lhs_read_only(node.expression, compressor) ? node : node.expression;
11606        }
11607        return node;
11608    }
11609
11610    function repeatable(compressor, node) {
11611        if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
11612        if (node instanceof AST_Sub) {
11613            return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
11614        }
11615        if (node instanceof AST_Symbol) return true;
11616        return !node.has_side_effects(compressor);
11617    }
11618
11619    function swap_chain(self, compressor) {
11620        var rhs = self.right.tail_node();
11621        if (rhs !== self.right) {
11622            var exprs = self.right.expressions.slice(0, -1);
11623            exprs.push(rhs.left);
11624            rhs = rhs.clone();
11625            rhs.left = make_sequence(self.right, exprs);
11626            self.right = rhs;
11627        }
11628        self.left = make_node(AST_Binary, self, {
11629            operator: self.operator,
11630            left: self.left,
11631            right: rhs.left,
11632            start: self.left.start,
11633            end: rhs.left.end,
11634        });
11635        self.right = rhs.right;
11636        if (compressor) {
11637            self.left = self.left.transform(compressor);
11638        } else if (self.operator == rhs.left.operator) {
11639            swap_chain(self.left);
11640        }
11641    }
11642
11643    OPT(AST_Binary, function(self, compressor) {
11644        if (commutativeOperators[self.operator]
11645            && self.right.is_constant()
11646            && !self.left.is_constant()
11647            && !(self.left instanceof AST_Binary
11648                && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
11649            // if right is a constant, whatever side effects the
11650            // left side might have could not influence the
11651            // result.  hence, force switch.
11652            reverse();
11653        }
11654        if (compressor.option("sequences")) {
11655            var seq = self.lift_sequences(compressor);
11656            if (seq !== self) return seq.optimize(compressor);
11657        }
11658        if (compressor.option("assignments") && lazy_op[self.operator]) {
11659            var lhs = extract_lhs(self.left, compressor);
11660            var right = self.right;
11661            // a || (a = x) ---> a = a || x
11662            // (a = x) && (a = y) ---> a = (a = x) && y
11663            if (lhs instanceof AST_SymbolRef
11664                && right instanceof AST_Assign
11665                && right.operator == "="
11666                && lhs.equals(right.left)) {
11667                lhs = lhs.clone();
11668                var assign = make_node(AST_Assign, self, {
11669                    operator: "=",
11670                    left: lhs,
11671                    right: make_node(AST_Binary, self, {
11672                        operator: self.operator,
11673                        left: self.left,
11674                        right: right.right,
11675                    }),
11676                });
11677                if (lhs.fixed) {
11678                    lhs.fixed = function() {
11679                        return assign.right;
11680                    };
11681                    lhs.fixed.assigns = [ assign ];
11682                }
11683                var def = lhs.definition();
11684                def.references.push(lhs);
11685                def.replaced++;
11686                return assign.optimize(compressor);
11687            }
11688        }
11689        if (compressor.option("comparisons")) switch (self.operator) {
11690          case "===":
11691          case "!==":
11692            if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
11693                AST_Node.warn("Expression always defined [{start}]", self);
11694                return make_sequence(self, [
11695                    self.right,
11696                    make_node(self.operator == "===" ? AST_False : AST_True, self),
11697                ]).optimize(compressor);
11698            }
11699            var is_strict_comparison = true;
11700            if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
11701                (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
11702                (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
11703                repeatable(compressor, self.left) && self.left.equals(self.right)) {
11704                self.operator = self.operator.slice(0, 2);
11705            }
11706            // XXX: intentionally falling down to the next case
11707          case "==":
11708          case "!=":
11709            // void 0 == x ---> null == x
11710            if (!is_strict_comparison && is_undefined(self.left, compressor)) {
11711                self.left = make_node(AST_Null, self.left);
11712            }
11713            // "undefined" == typeof x ---> undefined === x
11714            else if (compressor.option("typeofs")
11715                && self.left instanceof AST_String
11716                && self.left.value == "undefined"
11717                && self.right instanceof AST_UnaryPrefix
11718                && self.right.operator == "typeof") {
11719                var expr = self.right.expression;
11720                if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
11721                    : !(expr instanceof AST_PropAccess && compressor.option("ie"))) {
11722                    self.right = expr;
11723                    self.left = make_node(AST_Undefined, self.left).optimize(compressor);
11724                    if (self.operator.length == 2) self.operator += "=";
11725                }
11726            }
11727            // obj !== obj ---> false
11728            else if (self.left instanceof AST_SymbolRef
11729                && self.right instanceof AST_SymbolRef
11730                && self.left.definition() === self.right.definition()
11731                && is_object(self.left)) {
11732                return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
11733            }
11734            break;
11735          case "&&":
11736          case "||":
11737            // void 0 !== x && null !== x ---> null != x
11738            // void 0 === x || null === x ---> null == x
11739            var left = self.left;
11740            if (!(left instanceof AST_Binary)) break;
11741            if (left.operator != (self.operator == "&&" ? "!==" : "===")) break;
11742            if (!(self.right instanceof AST_Binary)) break;
11743            if (left.operator != self.right.operator) break;
11744            if (is_undefined(left.left, compressor) && self.right.left instanceof AST_Null
11745                || left.left instanceof AST_Null && is_undefined(self.right.left, compressor)) {
11746                var expr = left.right;
11747                if (expr instanceof AST_Assign && expr.operator == "=") expr = expr.left;
11748                if (expr.has_side_effects(compressor)) break;
11749                if (!expr.equals(self.right.right)) break;
11750                left.operator = left.operator.slice(0, -1);
11751                left.left = make_node(AST_Null, self);
11752                return left;
11753            }
11754            break;
11755        }
11756        var in_bool = false;
11757        var parent = compressor.parent();
11758        if (compressor.option("booleans")) {
11759            var lhs = extract_lhs(self.left, compressor);
11760            if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
11761                // a || a ---> a
11762                // (a = x) && a --> a = x
11763                if (lhs.equals(self.right)) {
11764                    return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor);
11765                }
11766                mark_duplicate_condition(compressor, lhs);
11767            }
11768            in_bool = compressor.in_boolean_context();
11769        }
11770        if (in_bool) switch (self.operator) {
11771          case "+":
11772            var ev = self.left.evaluate(compressor, true);
11773            if (ev && typeof ev == "string" || (ev = self.right.evaluate(compressor, true)) && typeof ev == "string") {
11774                AST_Node.warn("+ in boolean context always true [{start}]", self);
11775                var exprs = [];
11776                if (self.left.evaluate(compressor) instanceof AST_Node) exprs.push(self.left);
11777                if (self.right.evaluate(compressor) instanceof AST_Node) exprs.push(self.right);
11778                if (exprs.length < 2) {
11779                    exprs.push(make_node(AST_True, self));
11780                    return make_sequence(self, exprs).optimize(compressor);
11781                }
11782                self.truthy = true;
11783            }
11784            break;
11785          case "==":
11786            if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
11787                return make_node(AST_UnaryPrefix, self, {
11788                    operator: "!",
11789                    expression: self.right,
11790                }).optimize(compressor);
11791            }
11792            break;
11793          case "!=":
11794            if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
11795                return self.right.optimize(compressor);
11796            }
11797            break;
11798        }
11799        if (compressor.option("comparisons") && self.is_boolean(compressor)) {
11800            if (parent.TYPE != "Binary") {
11801                var negated = make_node(AST_UnaryPrefix, self, {
11802                    operator: "!",
11803                    expression: self.negate(compressor),
11804                });
11805                if (best_of(compressor, self, negated) === negated) return negated;
11806            }
11807            switch (self.operator) {
11808              case ">": reverse("<"); break;
11809              case ">=": reverse("<="); break;
11810            }
11811        }
11812        if (compressor.option("conditionals") && lazy_op[self.operator]) {
11813            if (self.left instanceof AST_Binary && self.operator == self.left.operator) {
11814                var before = make_node(AST_Binary, self, {
11815                    operator: self.operator,
11816                    left: self.left.right,
11817                    right: self.right,
11818                });
11819                var after = before.transform(compressor);
11820                if (before !== after) {
11821                    self.left = self.left.left;
11822                    self.right = after;
11823                }
11824            }
11825            // x && (y && z) ---> x && y && z
11826            // w || (x, y || z) ---> w || (x, y) || z
11827            var rhs = self.right.tail_node();
11828            if (rhs instanceof AST_Binary && self.operator == rhs.operator) swap_chain(self, compressor);
11829        }
11830        if (compressor.option("strings") && self.operator == "+") {
11831            // "foo" + 42 + "" ---> "foo" + 42
11832            if (self.right instanceof AST_String
11833                && self.right.value == ""
11834                && self.left.is_string(compressor)) {
11835                return self.left.optimize(compressor);
11836            }
11837            // "" + ("foo" + 42) ---> "foo" + 42
11838            if (self.left instanceof AST_String
11839                && self.left.value == ""
11840                && self.right.is_string(compressor)) {
11841                return self.right.optimize(compressor);
11842            }
11843            // "" + 42 + "foo" ---> 42 + "foo"
11844            if (self.left instanceof AST_Binary
11845                && self.left.operator == "+"
11846                && self.left.left instanceof AST_String
11847                && self.left.left.value == ""
11848                && self.right.is_string(compressor)
11849                && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) {
11850                self.left = self.left.right;
11851                return self.optimize(compressor);
11852            }
11853            // "x" + (y + "z") ---> "x" + y + "z"
11854            // w + (x, "y" + z) ---> w + (x, "y") + z
11855            var rhs = self.right.tail_node();
11856            if (rhs instanceof AST_Binary
11857                && self.operator == rhs.operator
11858                && (self.left.is_string(compressor) && rhs.is_string(compressor)
11859                    || rhs.left.is_string(compressor)
11860                        && (self.left.is_constant() || !rhs.right.has_side_effects(compressor)))) {
11861                swap_chain(self, compressor);
11862            }
11863        }
11864        if (compressor.option("evaluate")) {
11865            var associative = true;
11866            switch (self.operator) {
11867              case "&&":
11868                var ll = fuzzy_eval(compressor, self.left);
11869                if (!ll) {
11870                    AST_Node.warn("Condition left of && always false [{start}]", self);
11871                    return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor);
11872                } else if (!(ll instanceof AST_Node)) {
11873                    AST_Node.warn("Condition left of && always true [{start}]", self);
11874                    return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
11875                }
11876                if (!self.right.evaluate(compressor, true)) {
11877                    if (in_bool && !(self.right.evaluate(compressor) instanceof AST_Node)) {
11878                        AST_Node.warn("Boolean && always false [{start}]", self);
11879                        return make_sequence(self, [ self.left, make_node(AST_False, self) ]).optimize(compressor);
11880                    } else self.falsy = true;
11881                } else if ((in_bool || parent.operator == "&&" && parent.left === compressor.self())
11882                    && !(self.right.evaluate(compressor) instanceof AST_Node)) {
11883                    AST_Node.warn("Dropping side-effect-free && [{start}]", self);
11884                    return self.left.optimize(compressor);
11885                }
11886                // (x || false) && y ---> x ? y : false
11887                if (self.left.operator == "||") {
11888                    var lr = fuzzy_eval(compressor, self.left.right);
11889                    if (!lr) return make_node(AST_Conditional, self, {
11890                        condition: self.left.left,
11891                        consequent: self.right,
11892                        alternative: self.left.right,
11893                    }).optimize(compressor);
11894                }
11895                break;
11896              case "??":
11897                var nullish = true;
11898              case "||":
11899                var ll = fuzzy_eval(compressor, self.left, nullish);
11900                if (nullish ? ll == null : !ll) {
11901                    AST_Node.warn("Condition left of {operator} always {value} [{start}]", {
11902                        operator: self.operator,
11903                        value: nullish ? "nullish" : "false",
11904                        start: self.start,
11905                    });
11906                    return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
11907                } else if (!(ll instanceof AST_Node)) {
11908                    AST_Node.warn("Condition left of {operator} always {value} [{start}]", {
11909                        operator: self.operator,
11910                        value: nullish ? "defined" : "true",
11911                        start: self.start,
11912                    });
11913                    return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor);
11914                }
11915                var rr;
11916                if (!nullish && (rr = self.right.evaluate(compressor, true)) && !(rr instanceof AST_Node)) {
11917                    if (in_bool && !(self.right.evaluate(compressor) instanceof AST_Node)) {
11918                        AST_Node.warn("Boolean || always true [{start}]", self);
11919                        return make_sequence(self, [ self.left, make_node(AST_True, self) ]).optimize(compressor);
11920                    } else self.truthy = true;
11921                } else if ((in_bool || parent.operator == "||" && parent.left === compressor.self())
11922                    && !self.right.evaluate(compressor)) {
11923                    AST_Node.warn("Dropping side-effect-free {operator} [{start}]", self);
11924                    return self.left.optimize(compressor);
11925                }
11926                // x && true || y ---> x ? true : y
11927                if (!nullish && self.left.operator == "&&") {
11928                    var lr = fuzzy_eval(compressor, self.left.right);
11929                    if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
11930                        condition: self.left.left,
11931                        consequent: self.left.right,
11932                        alternative: self.right,
11933                    }).optimize(compressor);
11934                }
11935                break;
11936              case "+":
11937                // "foo" + ("bar" + x) ---> "foobar" + x
11938                if (self.left instanceof AST_Constant
11939                    && self.right instanceof AST_Binary
11940                    && self.right.operator == "+"
11941                    && self.right.left instanceof AST_Constant
11942                    && self.right.is_string(compressor)) {
11943                    self = make_node(AST_Binary, self, {
11944                        operator: "+",
11945                        left: make_node(AST_String, self.left, {
11946                            value: "" + self.left.value + self.right.left.value,
11947                            start: self.left.start,
11948                            end: self.right.left.end,
11949                        }),
11950                        right: self.right.right,
11951                    });
11952                }
11953                // (x + "foo") + "bar" ---> x + "foobar"
11954                if (self.right instanceof AST_Constant
11955                    && self.left instanceof AST_Binary
11956                    && self.left.operator == "+"
11957                    && self.left.right instanceof AST_Constant
11958                    && self.left.is_string(compressor)) {
11959                    self = make_node(AST_Binary, self, {
11960                        operator: "+",
11961                        left: self.left.left,
11962                        right: make_node(AST_String, self.right, {
11963                            value: "" + self.left.right.value + self.right.value,
11964                            start: self.left.right.start,
11965                            end: self.right.end,
11966                        }),
11967                    });
11968                }
11969                // a + -b ---> a - b
11970                if (self.right instanceof AST_UnaryPrefix
11971                    && self.right.operator == "-"
11972                    && self.left.is_number(compressor)) {
11973                    self = make_node(AST_Binary, self, {
11974                        operator: "-",
11975                        left: self.left,
11976                        right: self.right.expression,
11977                    });
11978                    break;
11979                }
11980                // -a + b ---> b - a
11981                if (self.left instanceof AST_UnaryPrefix
11982                    && self.left.operator == "-"
11983                    && reversible()
11984                    && self.right.is_number(compressor)) {
11985                    self = make_node(AST_Binary, self, {
11986                        operator: "-",
11987                        left: self.right,
11988                        right: self.left.expression,
11989                    });
11990                    break;
11991                }
11992                // (a + b) + 3 ---> 3 + (a + b)
11993                if (compressor.option("unsafe_math")
11994                    && self.left instanceof AST_Binary
11995                    && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
11996                    && self.right.is_constant()
11997                    && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
11998                    && self.left.is_number(compressor)
11999                    && !self.left.right.is_constant()
12000                    && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
12001                    self = make_node(AST_Binary, self, {
12002                        operator: self.left.operator,
12003                        left: make_node(AST_Binary, self, {
12004                            operator: self.operator,
12005                            left: self.right,
12006                            right: self.left.left,
12007                        }),
12008                        right: self.left.right,
12009                    });
12010                    break;
12011                }
12012              case "-":
12013                // a - -b ---> a + b
12014                if (self.right instanceof AST_UnaryPrefix
12015                    && self.right.operator == "-"
12016                    && self.left.is_number(compressor)
12017                    && self.right.expression.is_number(compressor)) {
12018                    self = make_node(AST_Binary, self, {
12019                        operator: "+",
12020                        left: self.left,
12021                        right: self.right.expression,
12022                    });
12023                    break;
12024                }
12025              case "*":
12026              case "/":
12027                associative = compressor.option("unsafe_math");
12028                // +a - b ---> a - b
12029                // a - +b ---> a - b
12030                if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
12031                    var node = self[operand];
12032                    if (node instanceof AST_UnaryPrefix && node.operator == "+") {
12033                        var exp = node.expression;
12034                        if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) {
12035                            self[operand] = exp;
12036                        }
12037                    }
12038                });
12039              case "&":
12040              case "|":
12041              case "^":
12042                // a + +b ---> +b + a
12043                if (self.operator != "-"
12044                    && self.operator != "/"
12045                    && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
12046                    && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
12047                    && reversible()
12048                    && !(self.left instanceof AST_Binary
12049                        && self.left.operator != self.operator
12050                        && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
12051                    self = best_of(compressor, self, make_node(AST_Binary, self, {
12052                        operator: self.operator,
12053                        left: self.right,
12054                        right: self.left,
12055                    }), self.right instanceof AST_Constant && !(self.left instanceof AST_Constant));
12056                }
12057                if (!associative || !self.is_number(compressor)) break;
12058                // a + (b + c) ---> (a + b) + c
12059                if (self.right instanceof AST_Binary
12060                    && self.right.operator != "%"
12061                    && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
12062                    && self.right.is_number(compressor)
12063                    && (self.operator != "+"
12064                        || self.right.left.is_boolean(compressor)
12065                        || self.right.left.is_number(compressor))
12066                    && (self.operator != "-" || !self.left.is_negative_zero())
12067                    && (self.right.left.is_constant_expression()
12068                        || !self.right.right.has_side_effects(compressor))
12069                    && !is_modify_array(self.right.right)) {
12070                    self = make_node(AST_Binary, self, {
12071                        operator: align(self.operator, self.right.operator),
12072                        left: make_node(AST_Binary, self.left, {
12073                            operator: self.operator,
12074                            left: self.left,
12075                            right: self.right.left,
12076                            start: self.left.start,
12077                            end: self.right.left.end,
12078                        }),
12079                        right: self.right.right,
12080                    });
12081                    if (self.operator == "+"
12082                        && !self.right.is_boolean(compressor)
12083                        && !self.right.is_number(compressor)) {
12084                        self.right = make_node(AST_UnaryPrefix, self.right, {
12085                            operator: "+",
12086                            expression: self.right,
12087                        });
12088                    }
12089                }
12090                // (2 * n) * 3 ---> 6 * n
12091                // (n + 2) + 3 ---> n + 5
12092                if (self.right instanceof AST_Constant
12093                    && self.left instanceof AST_Binary
12094                    && self.left.operator != "%"
12095                    && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
12096                    && self.left.is_number(compressor)) {
12097                    if (self.left.left instanceof AST_Constant) {
12098                        var lhs = make_binary(self.operator, self.left.left, self.right, {
12099                            start: self.left.left.start,
12100                            end: self.right.end,
12101                        });
12102                        self = make_binary(self.left.operator, try_evaluate(compressor, lhs), self.left.right, self);
12103                    } else if (self.left.right instanceof AST_Constant) {
12104                        var op = align(self.left.operator, self.operator);
12105                        var rhs = try_evaluate(compressor, make_binary(op, self.left.right, self.right, self.left));
12106                        if (rhs.is_constant()
12107                            && !(self.left.operator == "-"
12108                                && self.right.value != 0
12109                                && +rhs.value == 0
12110                                && self.left.left.is_negative_zero())) {
12111                            self = make_binary(self.left.operator, self.left.left, rhs, self);
12112                        }
12113                    }
12114                }
12115                break;
12116              case "instanceof":
12117                if (is_lambda(self.right)) return make_sequence(self, [
12118                    self,
12119                    make_node(AST_False, self),
12120                ]).optimize(compressor);
12121                break;
12122            }
12123            if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
12124                if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
12125                  // 0 + n ---> n
12126                  case "+":
12127                    if (self.left.value == 0) {
12128                        if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
12129                            operator: "+",
12130                            expression: self.right,
12131                        }).optimize(compressor);
12132                        if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
12133                    }
12134                    break;
12135                  // 1 * n ---> n
12136                  case "*":
12137                    if (self.left.value == 1) return make_node(AST_UnaryPrefix, self, {
12138                        operator: "+",
12139                        expression: self.right,
12140                    }).optimize(compressor);
12141                    break;
12142                }
12143                if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
12144                  // n + 0 ---> n
12145                  case "+":
12146                    if (self.right.value == 0) {
12147                        if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
12148                            operator: "+",
12149                            expression: self.left,
12150                        }).optimize(compressor);
12151                        if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
12152                    }
12153                    break;
12154                  // n - 0 ---> n
12155                  case "-":
12156                    if (self.right.value == 0) return make_node(AST_UnaryPrefix, self, {
12157                        operator: "+",
12158                        expression: self.left,
12159                    }).optimize(compressor);
12160                    break;
12161                  // n / 1 ---> n
12162                  case "/":
12163                    if (self.right.value == 1) return make_node(AST_UnaryPrefix, self, {
12164                        operator: "+",
12165                        expression: self.left,
12166                    }).optimize(compressor);
12167                    break;
12168                }
12169            }
12170        }
12171        if (compressor.option("typeofs")) switch (self.operator) {
12172          case "&&":
12173            mark_locally_defined(self.left, self.right, null);
12174            break;
12175          case "||":
12176            mark_locally_defined(self.left, null, self.right);
12177            break;
12178        }
12179        if (compressor.option("unsafe")) {
12180            var indexRight = is_indexFn(self.right);
12181            if (in_bool
12182                && indexRight
12183                && (self.operator == "==" || self.operator == "!=")
12184                && self.left instanceof AST_Number
12185                && self.left.value == 0) {
12186                return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
12187                    operator: "!",
12188                    expression: self.right,
12189                }) : self.right).optimize(compressor);
12190            }
12191            var indexLeft = is_indexFn(self.left);
12192            if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
12193                var node = make_node(AST_UnaryPrefix, self, {
12194                    operator: "!",
12195                    expression: make_node(AST_UnaryPrefix, self, {
12196                        operator: "~",
12197                        expression: indexLeft ? self.left : self.right,
12198                    }),
12199                });
12200                switch (self.operator) {
12201                  case "<":
12202                    if (indexLeft) break;
12203                  case "<=":
12204                  case "!=":
12205                    node = make_node(AST_UnaryPrefix, self, {
12206                        operator: "!",
12207                        expression: node,
12208                    });
12209                    break;
12210                }
12211                return node.optimize(compressor);
12212            }
12213        }
12214        return try_evaluate(compressor, self);
12215
12216        function is_modify_array(node) {
12217            var found = false;
12218            node.walk(new TreeWalker(function(node) {
12219                if (found) return true;
12220                if (node instanceof AST_Assign) {
12221                    if (node.left instanceof AST_PropAccess) return found = true;
12222                } else if (node instanceof AST_Unary) {
12223                    if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) {
12224                        return found = true;
12225                    }
12226                }
12227            }));
12228            return found;
12229        }
12230
12231        function align(ref, op) {
12232            switch (ref) {
12233              case "-":
12234                return op == "+" ? "-" : "+";
12235              case "/":
12236                return op == "*" ? "/" : "*";
12237              default:
12238                return op;
12239            }
12240        }
12241
12242        function make_binary(op, left, right, orig) {
12243            if (op == "+") {
12244                if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
12245                    left = make_node(AST_UnaryPrefix, left, {
12246                        operator: "+",
12247                        expression: left,
12248                    });
12249                }
12250                if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
12251                    right = make_node(AST_UnaryPrefix, right, {
12252                        operator: "+",
12253                        expression: right,
12254                    });
12255                }
12256            }
12257            return make_node(AST_Binary, orig, {
12258                operator: op,
12259                left: left,
12260                right: right,
12261            });
12262        }
12263
12264        function is_indexFn(node) {
12265            return node.TYPE == "Call"
12266                && node.expression instanceof AST_Dot
12267                && indexFns[node.expression.property];
12268        }
12269
12270        function is_indexOf_match_pattern() {
12271            switch (self.operator) {
12272              case "<=":
12273                // 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
12274                return indexRight && self.left instanceof AST_Number && self.left.value == 0;
12275              case "<":
12276                // array.indexOf(string) < 0 ---> !~array.indexOf(string)
12277                if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
12278                // -1 < array.indexOf(string) ---> !!~array.indexOf(string)
12279              case "==":
12280              case "!=":
12281                // -1 == array.indexOf(string) ---> !~array.indexOf(string)
12282                // -1 != array.indexOf(string) ---> !!~array.indexOf(string)
12283                if (!indexRight) return false;
12284                return self.left instanceof AST_Number && self.left.value == -1
12285                    || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
12286                        && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
12287            }
12288        }
12289
12290        function reversible() {
12291            return self.left.is_constant()
12292                || self.right.is_constant()
12293                || !self.left.has_side_effects(compressor)
12294                    && !self.right.has_side_effects(compressor);
12295        }
12296
12297        function reverse(op) {
12298            if (reversible()) {
12299                if (op) self.operator = op;
12300                var tmp = self.left;
12301                self.left = self.right;
12302                self.right = tmp;
12303            }
12304        }
12305    });
12306
12307    OPT(AST_SymbolExport, function(self) {
12308        return self;
12309    });
12310
12311    function recursive_ref(compressor, def, fn) {
12312        var level = 0, node = compressor.self();
12313        do {
12314            if (node === fn) return node;
12315            if (is_lambda(node) && node.name && node.name.definition() === def) return node;
12316        } while (node = compressor.parent(level++));
12317    }
12318
12319    function same_scope(def) {
12320        var scope = def.scope.resolve();
12321        return all(def.references, function(ref) {
12322            return scope === ref.scope.resolve();
12323        });
12324    }
12325
12326    OPT(AST_SymbolRef, function(self, compressor) {
12327        if (!compressor.option("ie")
12328            && is_undeclared_ref(self)
12329            // testing against `self.scope.uses_with` is an optimization
12330            && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
12331            switch (self.name) {
12332              case "undefined":
12333                return make_node(AST_Undefined, self).optimize(compressor);
12334              case "NaN":
12335                return make_node(AST_NaN, self).optimize(compressor);
12336              case "Infinity":
12337                return make_node(AST_Infinity, self).optimize(compressor);
12338            }
12339        }
12340        var parent = compressor.parent();
12341        if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
12342            var def = self.definition();
12343            var fixed = self.fixed_value();
12344            var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
12345            if (single_use) {
12346                if (is_lambda(fixed)) {
12347                    if ((def.scope !== self.scope.resolve(true) || def.in_loop)
12348                        && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
12349                        single_use = false;
12350                    } else if (def.redefined()) {
12351                        single_use = false;
12352                    } else if (recursive_ref(compressor, def, fixed)) {
12353                        single_use = false;
12354                    } else if (fixed.name && fixed.name.definition() !== def) {
12355                        single_use = false;
12356                    } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
12357                        if (!safe_from_strict_mode(fixed, compressor)) {
12358                            single_use = false;
12359                        } else if ((single_use = fixed.is_constant_expression(self.scope)) == "f") {
12360                            var scope = self.scope;
12361                            do {
12362                                if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
12363                                    scope.inlined = true;
12364                                }
12365                            } while (scope = scope.parent_scope);
12366                        }
12367                    } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
12368                        || fixed.name.name == "yield" && is_generator(fixed))) {
12369                        single_use = false;
12370                    } else if (fixed.has_side_effects(compressor)) {
12371                        single_use = false;
12372                    } else if (compressor.option("ie") && fixed instanceof AST_Class) {
12373                        single_use = false;
12374                    }
12375                    if (single_use) fixed.parent_scope = self.scope;
12376                } else if (!fixed
12377                    || def.recursive_refs > 0
12378                    || !fixed.is_constant_expression()
12379                    || fixed.drop_side_effect_free(compressor)) {
12380                    single_use = false;
12381                }
12382            }
12383            if (single_use) {
12384                def.single_use = false;
12385                fixed._squeezed = true;
12386                fixed.single_use = true;
12387                if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
12388                if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
12389                if (is_lambda(fixed)) {
12390                    var scopes = [];
12391                    var scope = self.scope;
12392                    do {
12393                        scopes.push(scope);
12394                        if (scope === def.scope) break;
12395                    } while (scope = scope.parent_scope);
12396                    fixed.enclosed.forEach(function(def) {
12397                        if (fixed.variables.has(def.name)) return;
12398                        for (var i = 0; i < scopes.length; i++) {
12399                            var scope = scopes[i];
12400                            if (!push_uniq(scope.enclosed, def)) return;
12401                            scope.var_names().set(def.name, true);
12402                        }
12403                    });
12404                }
12405                var value;
12406                if (def.recursive_refs > 0) {
12407                    value = fixed.clone(true);
12408                    var defun_def = value.name.definition();
12409                    var lambda_def = value.variables.get(value.name.name);
12410                    var name = lambda_def && lambda_def.orig[0];
12411                    var def_fn_name, symbol_type;
12412                    if (value instanceof AST_Class) {
12413                        def_fn_name = "def_function";
12414                        symbol_type = AST_SymbolClass;
12415                    } else {
12416                        def_fn_name = "def_variable";
12417                        symbol_type = AST_SymbolLambda;
12418                    }
12419                    if (!(name instanceof symbol_type)) {
12420                        name = make_node(symbol_type, value.name);
12421                        name.scope = value;
12422                        value.name = name;
12423                        lambda_def = value[def_fn_name](name);
12424                        lambda_def.recursive_refs = def.recursive_refs;
12425                    }
12426                    value.walk(new TreeWalker(function(node) {
12427                        if (node instanceof AST_SymbolDeclaration) {
12428                            if (node !== name) {
12429                                var def = node.definition();
12430                                def.orig.push(node);
12431                                def.eliminated++;
12432                            }
12433                            return;
12434                        }
12435                        if (!(node instanceof AST_SymbolRef)) return;
12436                        var def = node.definition();
12437                        if (def === defun_def) {
12438                            node.thedef = def = lambda_def;
12439                        } else {
12440                            def.single_use = false;
12441                            var fn = node.fixed_value();
12442                            if (is_lambda(fn)
12443                                && fn.name
12444                                && fn.name.definition() === def
12445                                && def.scope === fn.name.scope
12446                                && fixed.variables.get(fn.name.name) === def) {
12447                                fn.name = fn.name.clone();
12448                                node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
12449                            }
12450                        }
12451                        def.references.push(node);
12452                    }));
12453                } else {
12454                    if (fixed instanceof AST_Scope) {
12455                        compressor.push(fixed);
12456                        value = fixed.optimize(compressor);
12457                        compressor.pop();
12458                    } else {
12459                        value = fixed.optimize(compressor);
12460                    }
12461                    value = value.transform(new TreeTransformer(function(node, descend) {
12462                        if (node instanceof AST_Scope) return node;
12463                        node = node.clone();
12464                        descend(node, this);
12465                        return node;
12466                    }));
12467                }
12468                def.replaced++;
12469                return value;
12470            }
12471            var state;
12472            if (fixed && (state = self.fixed || def.fixed).should_replace !== false) {
12473                var ev, init;
12474                if (fixed instanceof AST_This) {
12475                    if (!is_funarg(def) && same_scope(def) && !cross_class(def)) init = fixed;
12476                } else if ((ev = fixed.evaluate(compressor, true)) !== fixed
12477                    && typeof ev != "function"
12478                    && (ev === null
12479                        || typeof ev != "object"
12480                        || compressor.option("unsafe_regexp")
12481                            && ev instanceof RegExp && !def.cross_loop && same_scope(def))) {
12482                    init = make_node_from_constant(ev, fixed);
12483                }
12484                if (init) {
12485                    if (state.should_replace === undefined) {
12486                        var value_length = init.optimize(compressor).print_to_string().length;
12487                        if (!has_symbol_ref(fixed)) {
12488                            value_length = Math.min(value_length, fixed.print_to_string().length);
12489                        }
12490                        var name_length = def.name.length;
12491                        if (compressor.option("unused") && !compressor.exposed(def)) {
12492                            var refs = def.references.length - def.replaced - def.assignments;
12493                            refs = Math.min(refs, def.references.filter(function(ref) {
12494                                return ref.fixed === state;
12495                            }).length);
12496                            name_length += (name_length + 2 + value_length) / Math.max(1, refs);
12497                        }
12498                        state.should_replace = value_length - Math.floor(name_length) < compressor.eval_threshold;
12499                    }
12500                    if (state.should_replace) {
12501                        var value;
12502                        if (has_symbol_ref(fixed)) {
12503                            value = init.optimize(compressor);
12504                            if (value === init) value = value.clone(true);
12505                        } else {
12506                            value = best_of_expression(init.optimize(compressor), fixed);
12507                            if (value === init || value === fixed) value = value.clone(true);
12508                        }
12509                        def.replaced++;
12510                        return value;
12511                    }
12512                }
12513            }
12514        }
12515        return self;
12516
12517        function cross_class(def) {
12518            var scope = self.scope;
12519            while (scope !== def.scope) {
12520                if (scope instanceof AST_Class) return true;
12521                scope = scope.parent_scope;
12522            }
12523        }
12524
12525        function has_symbol_ref(value) {
12526            var found;
12527            value.walk(new TreeWalker(function(node) {
12528                if (node instanceof AST_SymbolRef) found = true;
12529                if (found) return true;
12530            }));
12531            return found;
12532        }
12533    });
12534
12535    function is_raw_tag(compressor, tag) {
12536        return compressor.option("unsafe")
12537            && tag instanceof AST_Dot
12538            && tag.property == "raw"
12539            && is_undeclared_ref(tag.expression)
12540            && tag.expression.name == "String";
12541    }
12542
12543    function decode_template(str) {
12544        var malformed = false;
12545        str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
12546            var ch = decode_escape_sequence(seq);
12547            if (typeof ch == "string") return ch;
12548            malformed = true;
12549        });
12550        if (!malformed) return str;
12551    }
12552
12553    OPT(AST_Template, function(self, compressor) {
12554        if (!compressor.option("templates")) return self;
12555        var tag = self.tag;
12556        if (!tag || is_raw_tag(compressor, tag)) {
12557            var exprs = [];
12558            var strs = [];
12559            for (var i = 0, status; i < self.strings.length; i++) {
12560                var str = self.strings[i];
12561                if (!tag) {
12562                    var trimmed = decode_template(str);
12563                    if (trimmed) str = escape_literal(trimmed);
12564                }
12565                if (i > 0) {
12566                    var node = self.expressions[i - 1];
12567                    var value = should_join(node);
12568                    if (value) {
12569                        var prev = strs[strs.length - 1];
12570                        var joined = prev + value + str;
12571                        var decoded;
12572                        if (tag || typeof (decoded = decode_template(joined)) == status) {
12573                            strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined;
12574                            continue;
12575                        }
12576                    }
12577                    exprs.push(node);
12578                }
12579                strs.push(str);
12580                if (!tag) status = typeof trimmed;
12581            }
12582            if (!tag && strs.length > 1) {
12583                if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, {
12584                    operator: "+",
12585                    left: make_node(AST_Template, self, {
12586                        expressions: exprs.slice(0, -1),
12587                        strings: strs.slice(0, -1),
12588                    }).transform(compressor),
12589                    right: exprs[exprs.length - 1],
12590                }).optimize(compressor);
12591                if (strs[0] == "") {
12592                    var left = make_node(AST_Binary, self, {
12593                        operator: "+",
12594                        left: make_node(AST_String, self, { value: "" }),
12595                        right: exprs[0],
12596                    });
12597                    for (var i = 1; strs[i] == "" && i < exprs.length; i++) {
12598                        left = make_node(AST_Binary, self, {
12599                            operator: "+",
12600                            left: left,
12601                            right: exprs[i],
12602                        });
12603                    }
12604                    return best_of(compressor, self, make_node(AST_Binary, self, {
12605                        operator: "+",
12606                        left: left.transform(compressor),
12607                        right: make_node(AST_Template, self, {
12608                            expressions: exprs.slice(i),
12609                            strings: strs.slice(i),
12610                        }).transform(compressor),
12611                    }).optimize(compressor));
12612                }
12613            }
12614            self.expressions = exprs;
12615            self.strings = strs;
12616        }
12617        return try_evaluate(compressor, self);
12618
12619        function escape_literal(str) {
12620            return str.replace(/\r|\\|`|\${/g, function(s) {
12621                return "\\" + (s == "\r" ? "r" : s);
12622            });
12623        }
12624
12625        function should_join(node) {
12626            var ev = node.evaluate(compressor);
12627            if (ev === node) return;
12628            if (tag && /\r|\\|`/.test(ev)) return;
12629            ev = escape_literal("" + ev);
12630            if (ev.length > node.print_to_string().length + "${}".length) return;
12631            return ev;
12632        }
12633    });
12634
12635    function is_atomic(lhs, self) {
12636        return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
12637    }
12638
12639    OPT(AST_Undefined, function(self, compressor) {
12640        if (compressor.option("unsafe_undefined")) {
12641            var undef = find_scope(compressor).find_variable("undefined");
12642            if (undef) {
12643                var ref = make_node(AST_SymbolRef, self, {
12644                    name: "undefined",
12645                    scope: undef.scope,
12646                    thedef: undef,
12647                });
12648                ref.is_undefined = true;
12649                return ref;
12650            }
12651        }
12652        var lhs = is_lhs(compressor.self(), compressor.parent());
12653        if (lhs && is_atomic(lhs, self)) return self;
12654        return make_node(AST_UnaryPrefix, self, {
12655            operator: "void",
12656            expression: make_node(AST_Number, self, { value: 0 }),
12657        });
12658    });
12659
12660    OPT(AST_Infinity, function(self, compressor) {
12661        var lhs = is_lhs(compressor.self(), compressor.parent());
12662        if (lhs && is_atomic(lhs, self)) return self;
12663        if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) {
12664            return self;
12665        }
12666        return make_node(AST_Binary, self, {
12667            operator: "/",
12668            left: make_node(AST_Number, self, { value: 1 }),
12669            right: make_node(AST_Number, self, { value: 0 }),
12670        });
12671    });
12672
12673    OPT(AST_NaN, function(self, compressor) {
12674        var lhs = is_lhs(compressor.self(), compressor.parent());
12675        if (lhs && is_atomic(lhs, self)) return self;
12676        if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
12677        return make_node(AST_Binary, self, {
12678            operator: "/",
12679            left: make_node(AST_Number, self, { value: 0 }),
12680            right: make_node(AST_Number, self, { value: 0 }),
12681        });
12682    });
12683
12684    function is_reachable(self, defs) {
12685        var reachable = false;
12686        var find_ref = new TreeWalker(function(node) {
12687            if (reachable) return true;
12688            if (node instanceof AST_SymbolRef && member(node.definition(), defs)) return reachable = true;
12689        });
12690        var scan_scope = new TreeWalker(function(node) {
12691            if (reachable) return true;
12692            if (node instanceof AST_Lambda && node !== self) {
12693                if (!(node.name || is_async(node) || is_generator(node))) {
12694                    var parent = scan_scope.parent();
12695                    if (parent instanceof AST_Call && parent.expression === node) return;
12696                }
12697                node.walk(find_ref);
12698                return true;
12699            }
12700        });
12701        self.walk(scan_scope);
12702        return reachable;
12703    }
12704
12705    var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
12706    var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
12707    OPT(AST_Assign, function(self, compressor) {
12708        if (compressor.option("dead_code")) {
12709            if (self.left instanceof AST_PropAccess) {
12710                if (self.operator == "=") {
12711                    if (self.redundant) {
12712                        var exprs = [ self.left.expression ];
12713                        if (self.left instanceof AST_Sub) exprs.push(self.left.property);
12714                        exprs.push(self.right);
12715                        return make_sequence(self, exprs).optimize(compressor);
12716                    }
12717                    if (self.left.equals(self.right) && !self.left.has_side_effects(compressor)) {
12718                        return self.right;
12719                    }
12720                    var exp = self.left.expression;
12721                    if (exp instanceof AST_Lambda
12722                        || !compressor.has_directive("use strict")
12723                            && exp instanceof AST_Constant
12724                            && !exp.may_throw_on_access(compressor)) {
12725                        return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
12726                            self.left.property,
12727                            self.right
12728                        ]).optimize(compressor);
12729                    }
12730                }
12731            } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) {
12732                var parent;
12733                if (self.operator == "=" && self.left.equals(self.right)
12734                    && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
12735                    return self.right;
12736                }
12737                if (self.left.is_immutable()) return strip_assignment();
12738                var def = self.left.definition();
12739                var scope = def.scope.resolve();
12740                var local = scope === compressor.find_parent(AST_Lambda);
12741                var level = 0, node;
12742                parent = compressor.self();
12743                if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do {
12744                    node = parent;
12745                    parent = compressor.parent(level++);
12746                    if (parent instanceof AST_Assign) {
12747                        if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) {
12748                            if (in_try(level, parent, !local)) break;
12749                            return strip_assignment(def);
12750                        }
12751                        if (parent.left.match_symbol(function(node) {
12752                            if (node instanceof AST_PropAccess) return true;
12753                        })) break;
12754                        continue;
12755                    }
12756                    if (parent instanceof AST_Exit) {
12757                        if (!local) break;
12758                        if (in_try(level, parent)) break;
12759                        if (is_reachable(scope, [ def ])) break;
12760                        return strip_assignment(def);
12761                    }
12762                    if (parent instanceof AST_SimpleStatement) {
12763                        if (!local) break;
12764                        if (is_reachable(scope, [ def ])) break;
12765                        var stat;
12766                        do {
12767                            stat = parent;
12768                            parent = compressor.parent(level++);
12769                            if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def);
12770                        } while (is_tail_block(stat, parent));
12771                        break;
12772                    }
12773                    if (parent instanceof AST_VarDef) {
12774                        if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
12775                        if (parent.name.definition() !== def) continue;
12776                        if (in_try(level, parent)) break;
12777                        return strip_assignment(def);
12778                    }
12779                } while (is_tail(node, parent));
12780            }
12781        }
12782        if (compressor.option("sequences")) {
12783            var seq = self.lift_sequences(compressor);
12784            if (seq !== self) return seq.optimize(compressor);
12785        }
12786        if (compressor.option("assignments")) {
12787            if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
12788                // x = expr1 OP expr2
12789                if (self.right.left instanceof AST_SymbolRef
12790                    && self.right.left.name == self.left.name
12791                    && ASSIGN_OPS[self.right.operator]) {
12792                    // x = x - 2 ---> x -= 2
12793                    return make_compound(self.right.right);
12794                }
12795                if (self.right.right instanceof AST_SymbolRef
12796                    && self.right.right.name == self.left.name
12797                    && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
12798                    && !self.right.left.has_side_effects(compressor)) {
12799                    // x = 2 & x ---> x &= 2
12800                    return make_compound(self.right.left);
12801                }
12802            }
12803            if ((self.operator == "-=" || self.operator == "+="
12804                    && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
12805                && self.right instanceof AST_Number
12806                && self.right.value == 1) {
12807                var op = self.operator.slice(0, -1);
12808                return make_node(AST_UnaryPrefix, self, {
12809                    operator: op + op,
12810                    expression: self.left,
12811                });
12812            }
12813        }
12814        return try_evaluate(compressor, self);
12815
12816        function is_tail(node, parent) {
12817            if (parent instanceof AST_Binary) switch (node) {
12818              case parent.left:
12819                return parent.right.is_constant_expression(scope);
12820              case parent.right:
12821                return true;
12822              default:
12823                return false;
12824            }
12825            if (parent instanceof AST_Conditional) switch (node) {
12826              case parent.condition:
12827                return parent.consequent.is_constant_expression(scope)
12828                    && parent.alternative.is_constant_expression(scope);
12829              case parent.consequent:
12830              case parent.alternative:
12831                return true;
12832              default:
12833                return false;
12834            }
12835            if (parent instanceof AST_Sequence) {
12836                var exprs = parent.expressions;
12837                var stop = exprs.indexOf(node);
12838                if (stop < 0) return false;
12839                for (var i = exprs.length; --i > stop;) {
12840                    if (!exprs[i].is_constant_expression(scope)) return false;
12841                }
12842                return true;
12843            }
12844            return parent instanceof AST_UnaryPrefix;
12845        }
12846
12847        function is_tail_block(stat, parent) {
12848            if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat);
12849            if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat);
12850            if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat);
12851            if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat;
12852            if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
12853        }
12854
12855        function in_try(level, node, sync) {
12856            var right = self.right;
12857            self.right = make_node(AST_Null, right);
12858            var may_throw = node.may_throw(compressor);
12859            self.right = right;
12860            return find_try(compressor, level, node, scope, may_throw, sync);
12861        }
12862
12863        function make_compound(rhs) {
12864            var fixed = self.left.fixed;
12865            if (fixed) fixed.to_binary = replace_ref(function(node) {
12866                return node.left;
12867            }, fixed);
12868            return make_node(AST_Assign, self, {
12869                operator: self.right.operator + "=",
12870                left: self.left,
12871                right: rhs,
12872            });
12873        }
12874
12875        function strip_assignment(def) {
12876            if (def) def.fixed = false;
12877            return (self.operator != "=" ? make_node(AST_Binary, self, {
12878                operator: self.operator.slice(0, -1),
12879                left: self.left,
12880                right: self.right,
12881            }) : maintain_this_binding(compressor.parent(), self, self.right)).optimize(compressor);
12882        }
12883    });
12884
12885    OPT(AST_Conditional, function(self, compressor) {
12886        if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
12887            var expressions = self.condition.expressions.slice();
12888            var node = self.clone();
12889            node.condition = expressions.pop();
12890            expressions.push(node);
12891            return make_sequence(self, expressions).optimize(compressor);
12892        }
12893        if (!compressor.option("conditionals")) return self;
12894        var condition = self.condition;
12895        if (compressor.option("booleans") && !condition.has_side_effects(compressor)) {
12896            mark_duplicate_condition(compressor, condition);
12897        }
12898        condition = fuzzy_eval(compressor, condition);
12899        if (!condition) {
12900            AST_Node.warn("Condition always false [{start}]", self);
12901            return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
12902        } else if (!(condition instanceof AST_Node)) {
12903            AST_Node.warn("Condition always true [{start}]", self);
12904            return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
12905        }
12906        var first = first_in_statement(compressor);
12907        var negated = condition.negate(compressor, first);
12908        if ((first ? best_of_statement : best_of_expression)(condition, negated) === negated) {
12909            self = make_node(AST_Conditional, self, {
12910                condition: negated,
12911                consequent: self.alternative,
12912                alternative: self.consequent,
12913            });
12914            negated = condition;
12915            condition = self.condition;
12916        }
12917        var consequent = self.consequent;
12918        var alternative = self.alternative;
12919        var cond_lhs = extract_lhs(condition, compressor);
12920        if (repeatable(compressor, cond_lhs)) {
12921            // x ? x : y ---> x || y
12922            if (cond_lhs.equals(consequent)) return make_node(AST_Binary, self, {
12923                operator: "||",
12924                left: condition,
12925                right: alternative,
12926            }).optimize(compressor);
12927            // x ? y : x ---> x && y
12928            if (cond_lhs.equals(alternative)) return make_node(AST_Binary, self, {
12929                operator: "&&",
12930                left: condition,
12931                right: consequent,
12932            }).optimize(compressor);
12933        }
12934        // if (foo) exp = something; else exp = something_else;
12935        //                   |
12936        //                   v
12937        // exp = foo ? something : something_else;
12938        var seq_tail = consequent.tail_node();
12939        if (seq_tail instanceof AST_Assign) {
12940            var is_eq = seq_tail.operator == "=";
12941            var alt_tail = is_eq ? alternative.tail_node() : alternative;
12942            if ((is_eq || consequent === seq_tail)
12943                && alt_tail instanceof AST_Assign
12944                && seq_tail.operator == alt_tail.operator
12945                && seq_tail.left.equals(alt_tail.left)
12946                && (is_eq && seq_tail.left instanceof AST_SymbolRef
12947                    || !condition.has_side_effects(compressor)
12948                        && can_shift_lhs_of_tail(consequent)
12949                        && can_shift_lhs_of_tail(alternative))) {
12950                return make_node(AST_Assign, self, {
12951                    operator: seq_tail.operator,
12952                    left: seq_tail.left,
12953                    right: make_node(AST_Conditional, self, {
12954                        condition: condition,
12955                        consequent: pop_lhs(consequent),
12956                        alternative: pop_lhs(alternative),
12957                    }),
12958                });
12959            }
12960        }
12961        var alt_tail = alternative.tail_node();
12962        // x ? y : y ---> x, y
12963        // x ? (a, c) : (b, c) ---> x ? a : b, c
12964        if (seq_tail.equals(alt_tail)) return make_sequence(self, consequent.equals(alternative) ? [
12965            condition,
12966            consequent,
12967        ] : [
12968            make_node(AST_Conditional, self, {
12969                condition: condition,
12970                consequent: pop_seq(consequent),
12971                alternative: pop_seq(alternative),
12972            }),
12973            alt_tail,
12974        ]).optimize(compressor);
12975        // x ? y.p : z.p ---> (x ? y : z).p
12976        // x ? y(a) : z(a) ---> (x ? y : z)(a)
12977        // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
12978        var combined = combine_tail(consequent, alternative, true);
12979        if (combined) return combined;
12980        // x ? y(a) : y(b) ---> y(x ? a : b)
12981        var arg_index;
12982        if (consequent instanceof AST_Call
12983            && alternative.TYPE == consequent.TYPE
12984            && (arg_index = arg_diff(consequent, alternative)) >= 0
12985            && consequent.expression.equals(alternative.expression)
12986            && !condition.has_side_effects(compressor)
12987            && !consequent.expression.has_side_effects(compressor)) {
12988            var node = consequent.clone();
12989            var arg = consequent.args[arg_index];
12990            node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, {
12991                expression: make_node(AST_Conditional, self, {
12992                    condition: condition,
12993                    consequent: arg.expression,
12994                    alternative: alternative.args[arg_index].expression,
12995                }),
12996            }) : make_node(AST_Conditional, self, {
12997                condition: condition,
12998                consequent: arg,
12999                alternative: alternative.args[arg_index],
13000            });
13001            return node;
13002        }
13003        // x ? (y ? a : b) : b ---> x && y ? a : b
13004        if (seq_tail instanceof AST_Conditional
13005            && seq_tail.alternative.equals(alternative)) {
13006            return make_node(AST_Conditional, self, {
13007                condition: make_node(AST_Binary, self, {
13008                    left: condition,
13009                    operator: "&&",
13010                    right: fuse(consequent, seq_tail, "condition"),
13011                }),
13012                consequent: seq_tail.consequent,
13013                alternative: merge_expression(seq_tail.alternative, alternative),
13014            });
13015        }
13016        // x ? (y ? a : b) : a ---> !x || y ? a : b
13017        if (seq_tail instanceof AST_Conditional
13018            && seq_tail.consequent.equals(alternative)) {
13019            return make_node(AST_Conditional, self, {
13020                condition: make_node(AST_Binary, self, {
13021                    left: negated,
13022                    operator: "||",
13023                    right: fuse(consequent, seq_tail, "condition"),
13024                }),
13025                consequent: merge_expression(seq_tail.consequent, alternative),
13026                alternative: seq_tail.alternative,
13027            });
13028        }
13029        // x ? a : (y ? a : b) ---> x || y ? a : b
13030        if (alt_tail instanceof AST_Conditional
13031            && consequent.equals(alt_tail.consequent)) {
13032            return make_node(AST_Conditional, self, {
13033                condition: make_node(AST_Binary, self, {
13034                    left: condition,
13035                    operator: "||",
13036                    right: fuse(alternative, alt_tail, "condition"),
13037                }),
13038                consequent: merge_expression(consequent, alt_tail.consequent),
13039                alternative: alt_tail.alternative,
13040            });
13041        }
13042        // x ? b : (y ? a : b) ---> !x && y ? a : b
13043        if (alt_tail instanceof AST_Conditional
13044            && consequent.equals(alt_tail.alternative)) {
13045            return make_node(AST_Conditional, self, {
13046                condition: make_node(AST_Binary, self, {
13047                    left: negated,
13048                    operator: "&&",
13049                    right: fuse(alternative, alt_tail, "condition"),
13050                }),
13051                consequent: alt_tail.consequent,
13052                alternative: merge_expression(consequent, alt_tail.alternative),
13053            });
13054        }
13055        // x ? y && a : a ---> (!x || y) && a
13056        if (seq_tail instanceof AST_Binary
13057            && seq_tail.operator == "&&"
13058            && seq_tail.right.equals(alternative)) {
13059            return make_node(AST_Binary, self, {
13060                operator: "&&",
13061                left: make_node(AST_Binary, self, {
13062                    operator: "||",
13063                    left: negated,
13064                    right: fuse(consequent, seq_tail, "left"),
13065                }),
13066                right: merge_expression(seq_tail.right, alternative),
13067            }).optimize(compressor);
13068        }
13069        // x ? y || a : a ---> x && y || a
13070        if (seq_tail instanceof AST_Binary
13071            && seq_tail.operator == "||"
13072            && seq_tail.right.equals(alternative)) {
13073            return make_node(AST_Binary, self, {
13074                operator: "||",
13075                left: make_node(AST_Binary, self, {
13076                    operator: "&&",
13077                    left: condition,
13078                    right: fuse(consequent, seq_tail, "left"),
13079                }),
13080                right: merge_expression(seq_tail.right, alternative),
13081            }).optimize(compressor);
13082        }
13083        // x ? a : y && a ---> (x || y) && a
13084        if (alt_tail instanceof AST_Binary
13085            && alt_tail.operator == "&&"
13086            && alt_tail.right.equals(consequent)) {
13087            return make_node(AST_Binary, self, {
13088                operator: "&&",
13089                left: make_node(AST_Binary, self, {
13090                    operator: "||",
13091                    left: condition,
13092                    right: fuse(alternative, alt_tail, "left"),
13093                }),
13094                right: merge_expression(consequent, alt_tail.right),
13095            }).optimize(compressor);
13096        }
13097        // x ? a : y || a ---> !x && y || a
13098        if (alt_tail instanceof AST_Binary
13099            && alt_tail.operator == "||"
13100            && alt_tail.right.equals(consequent)) {
13101            return make_node(AST_Binary, self, {
13102                operator: "||",
13103                left: make_node(AST_Binary, self, {
13104                    operator: "&&",
13105                    left: negated,
13106                    right: fuse(alternative, alt_tail, "left"),
13107                }),
13108                right: merge_expression(consequent, alt_tail.right),
13109            }).optimize(compressor);
13110        }
13111        var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
13112        if (is_true(consequent)) {
13113            // c ? true : false ---> !!c
13114            if (is_false(alternative)) return booleanize(condition);
13115            // c ? true : x ---> !!c || x
13116            return make_node(AST_Binary, self, {
13117                operator: "||",
13118                left: booleanize(condition),
13119                right: alternative,
13120            }).optimize(compressor);
13121        }
13122        if (is_false(consequent)) {
13123            // c ? false : true ---> !c
13124            if (is_true(alternative)) return booleanize(condition.negate(compressor));
13125            // c ? false : x ---> !c && x
13126            return make_node(AST_Binary, self, {
13127                operator: "&&",
13128                left: booleanize(condition.negate(compressor)),
13129                right: alternative,
13130            }).optimize(compressor);
13131        }
13132        // c ? x : true ---> !c || x
13133        if (is_true(alternative)) return make_node(AST_Binary, self, {
13134            operator: "||",
13135            left: booleanize(condition.negate(compressor)),
13136            right: consequent,
13137        }).optimize(compressor);
13138        // c ? x : false ---> !!c && x
13139        if (is_false(alternative)) return make_node(AST_Binary, self, {
13140            operator: "&&",
13141            left: booleanize(condition),
13142            right: consequent,
13143        }).optimize(compressor);
13144        if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
13145        return self;
13146
13147        function booleanize(node) {
13148            if (node.is_boolean(compressor)) return node;
13149            // !!expression
13150            return make_node(AST_UnaryPrefix, node, {
13151                operator: "!",
13152                expression: node.negate(compressor),
13153            });
13154        }
13155
13156        // AST_True or !0
13157        function is_true(node) {
13158            return node instanceof AST_True
13159                || in_bool
13160                    && node instanceof AST_Constant
13161                    && node.value
13162                || (node instanceof AST_UnaryPrefix
13163                    && node.operator == "!"
13164                    && node.expression instanceof AST_Constant
13165                    && !node.expression.value);
13166        }
13167        // AST_False or !1 or void 0
13168        function is_false(node) {
13169            return node instanceof AST_False
13170                || in_bool
13171                    && (node instanceof AST_Constant
13172                            && !node.value
13173                        || node instanceof AST_UnaryPrefix
13174                            && node.operator == "void"
13175                            && !node.expression.has_side_effects(compressor))
13176                || (node instanceof AST_UnaryPrefix
13177                    && node.operator == "!"
13178                    && node.expression instanceof AST_Constant
13179                    && node.expression.value);
13180        }
13181
13182        function arg_diff(consequent, alternative) {
13183            var a = consequent.args;
13184            var b = alternative.args;
13185            var len = a.length;
13186            if (len != b.length) return -2;
13187            for (var i = 0; i < len; i++) {
13188                if (!a[i].equals(b[i])) {
13189                    if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
13190                    for (var j = i + 1; j < len; j++) {
13191                        if (!a[j].equals(b[j])) return -2;
13192                    }
13193                    return i;
13194                }
13195            }
13196            return -1;
13197        }
13198
13199        function fuse(node, tail, prop) {
13200            if (node === tail) return tail[prop];
13201            var exprs = node.expressions.slice(0, -1);
13202            exprs.push(tail[prop]);
13203            return make_sequence(node, exprs);
13204        }
13205
13206        function is_tail_equivalent(consequent, alternative) {
13207            if (consequent.TYPE != alternative.TYPE) return;
13208            if (consequent.optional != alternative.optional) return;
13209            if (consequent instanceof AST_Call) {
13210                if (arg_diff(consequent, alternative) != -1) return;
13211                return consequent.TYPE != "Call"
13212                    || !(consequent.expression instanceof AST_PropAccess
13213                        || alternative.expression instanceof AST_PropAccess)
13214                    || is_tail_equivalent(consequent.expression, alternative.expression);
13215            }
13216            if (!(consequent instanceof AST_PropAccess)) return;
13217            var p = consequent.property;
13218            var q = alternative.property;
13219            return (p instanceof AST_Node ? p.equals(q) : p == q)
13220                && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
13221        }
13222
13223        function combine_tail(consequent, alternative, top) {
13224            var seq_tail = consequent.tail_node();
13225            var alt_tail = alternative.tail_node();
13226            if (!is_tail_equivalent(seq_tail, alt_tail)) return !top && make_node(AST_Conditional, self, {
13227                condition: condition,
13228                consequent: consequent,
13229                alternative: alternative,
13230            });
13231            var node = seq_tail.clone();
13232            var seq_expr = fuse(consequent, seq_tail, "expression");
13233            var alt_expr = fuse(alternative, alt_tail, "expression");
13234            var combined = combine_tail(seq_expr, alt_expr);
13235            if (seq_tail.expression instanceof AST_Sequence) {
13236                combined = maintain_this_binding(seq_tail, seq_tail.expression, combined);
13237            }
13238            node.expression = combined;
13239            return node;
13240        }
13241
13242        function can_shift_lhs_of_tail(node) {
13243            return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
13244                return !expr.has_side_effects(compressor);
13245            });
13246        }
13247
13248        function pop_lhs(node) {
13249            if (!(node instanceof AST_Sequence)) return node.right;
13250            var exprs = node.expressions.slice();
13251            exprs.push(exprs.pop().right);
13252            return make_sequence(node, exprs);
13253        }
13254
13255        function pop_seq(node) {
13256            if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { value: 0 });
13257            return make_sequence(node, node.expressions.slice(0, -1));
13258        }
13259    });
13260
13261    OPT(AST_Boolean, function(self, compressor) {
13262        if (!compressor.option("booleans")) return self;
13263        if (compressor.in_boolean_context()) return make_node(AST_Number, self, { value: +self.value });
13264        var p = compressor.parent();
13265        if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
13266            AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{start}]", {
13267                operator: p.operator,
13268                value: self.value,
13269                start: p.start,
13270            });
13271            return make_node(AST_Number, self, { value: +self.value });
13272        }
13273        return make_node(AST_UnaryPrefix, self, {
13274            operator: "!",
13275            expression: make_node(AST_Number, self, { value: 1 - self.value }),
13276        });
13277    });
13278
13279    OPT(AST_Spread, function(self, compressor) {
13280        var exp = self.expression;
13281        if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
13282            return List.splice(exp.elements.map(function(node) {
13283                return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
13284            }));
13285        }
13286        return self;
13287    });
13288
13289    function safe_to_flatten(value, compressor) {
13290        if (!value) return false;
13291        var parent = compressor.parent();
13292        if (parent.TYPE != "Call") return true;
13293        if (parent.expression !== compressor.self()) return true;
13294        if (value instanceof AST_SymbolRef) {
13295            value = value.fixed_value();
13296            if (!value) return false;
13297        }
13298        return value instanceof AST_Lambda && !value.contains_this();
13299    }
13300
13301    OPT(AST_Sub, function(self, compressor) {
13302        var expr = self.expression;
13303        var prop = self.property;
13304        var terminated = trim_optional_chain(self, compressor);
13305        if (terminated) return terminated;
13306        if (compressor.option("properties")) {
13307            var key = prop.evaluate(compressor);
13308            if (key !== prop) {
13309                if (typeof key == "string") {
13310                    if (key == "undefined") {
13311                        key = undefined;
13312                    } else {
13313                        var value = parseFloat(key);
13314                        if (value.toString() == key) {
13315                            key = value;
13316                        }
13317                    }
13318                }
13319                prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
13320                var property = "" + key;
13321                if (is_identifier_string(property)
13322                    && property.length <= prop.print_to_string().length + 1) {
13323                    return make_node(AST_Dot, self, {
13324                        optional: self.optional,
13325                        expression: expr,
13326                        property: property,
13327                        quoted: true,
13328                    }).optimize(compressor);
13329                }
13330            }
13331        }
13332        var parent = compressor.parent();
13333        var assigned = is_lhs(compressor.self(), parent);
13334        var def, fn, fn_parent, index;
13335        if (compressor.option("arguments")
13336            && expr instanceof AST_SymbolRef
13337            && is_arguments(def = expr.definition())
13338            && !expr.in_arg
13339            && prop instanceof AST_Number
13340            && Math.floor(index = prop.value) == index
13341            && (fn = def.scope) === find_lambda()
13342            && fn.uses_arguments < (assigned ? 2 : 3)) {
13343            if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
13344                if (!def.deleted) def.deleted = [];
13345                def.deleted[index] = true;
13346            }
13347            var argname = fn.argnames[index];
13348            if (def.deleted && def.deleted[index]) {
13349                argname = null;
13350            } else if (argname) {
13351                var arg_def;
13352                if (!(argname instanceof AST_SymbolFunarg)
13353                    || argname.name == "await"
13354                    || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) {
13355                    argname = null;
13356                } else if (compressor.has_directive("use strict")
13357                    || fn.name
13358                    || fn.rest
13359                    || !(fn_parent instanceof AST_Call
13360                        && index < fn_parent.args.length
13361                        && all(fn_parent.args.slice(0, index + 1), function(arg) {
13362                            return !(arg instanceof AST_Spread);
13363                        }))
13364                    || !all(fn.argnames, function(argname) {
13365                        return argname instanceof AST_SymbolFunarg;
13366                    })) {
13367                    if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
13368                }
13369            } else if ((assigned || !has_reassigned())
13370                && index < fn.argnames.length + 5
13371                && compressor.drop_fargs(fn, fn_parent)
13372                && !fn.rest) {
13373                while (index >= fn.argnames.length) {
13374                    argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
13375                    fn.argnames.push(argname);
13376                }
13377            }
13378            if (argname && find_if(function(node) {
13379                return node.name === argname.name;
13380            }, fn.argnames) === argname) {
13381                if (assigned) def.reassigned--;
13382                var sym = make_node(AST_SymbolRef, argname);
13383                sym.reference();
13384                argname.unused = undefined;
13385                return sym;
13386            }
13387        }
13388        if (assigned) return self;
13389        if (compressor.option("sequences")
13390            && parent.TYPE != "Call"
13391            && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
13392            var seq = lift_sequence_in_expression(self, compressor);
13393            if (seq !== self) return seq.optimize(compressor);
13394        }
13395        if (key !== prop) {
13396            var sub = self.flatten_object(property, compressor);
13397            if (sub) {
13398                expr = self.expression = sub.expression;
13399                prop = self.property = sub.property;
13400            }
13401        }
13402        var elements;
13403        if (compressor.option("properties")
13404            && compressor.option("side_effects")
13405            && prop instanceof AST_Number
13406            && expr instanceof AST_Array
13407            && all(elements = expr.elements, function(value) {
13408                return !(value instanceof AST_Spread);
13409            })) {
13410            var index = prop.value;
13411            var retValue = elements[index];
13412            if (safe_to_flatten(retValue, compressor)) {
13413                var is_hole = retValue instanceof AST_Hole;
13414                var flatten = !is_hole;
13415                var values = [];
13416                for (var i = elements.length; --i > index;) {
13417                    var value = elements[i].drop_side_effect_free(compressor);
13418                    if (value) {
13419                        values.unshift(value);
13420                        if (flatten && value.has_side_effects(compressor)) flatten = false;
13421                    }
13422                }
13423                if (!flatten) values.unshift(retValue);
13424                while (--i >= 0) {
13425                    var value = elements[i].drop_side_effect_free(compressor);
13426                    if (value) {
13427                        values.unshift(value);
13428                    } else if (is_hole) {
13429                        values.unshift(make_node(AST_Hole, elements[i]));
13430                    } else {
13431                        index--;
13432                    }
13433                }
13434                if (flatten) {
13435                    values.push(retValue);
13436                    return make_sequence(self, values).optimize(compressor);
13437                }
13438                return make_node(AST_Sub, self, {
13439                    expression: make_node(AST_Array, expr, { elements: values }),
13440                    property: make_node(AST_Number, prop, { value: index }),
13441                });
13442            }
13443        }
13444        return try_evaluate(compressor, self);
13445
13446        function find_lambda() {
13447            var i = 0, p;
13448            while (p = compressor.parent(i++)) {
13449                if (p instanceof AST_Lambda) {
13450                    if (p instanceof AST_Accessor) return;
13451                    if (is_arrow(p)) continue;
13452                    fn_parent = compressor.parent(i);
13453                    return p;
13454                }
13455            }
13456        }
13457
13458        function has_reassigned() {
13459            return !compressor.option("reduce_vars") || def.reassigned;
13460        }
13461    });
13462
13463    AST_LambdaExpression.DEFMETHOD("contains_super", function() {
13464        var result = false;
13465        var self = this;
13466        self.walk(new TreeWalker(function(node) {
13467            if (result) return true;
13468            if (node instanceof AST_Super) return result = true;
13469            if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
13470        }));
13471        return result;
13472    });
13473
13474    // contains_this()
13475    // returns false only if context bound by the specified scope (or scope
13476    // containing the specified expression) is not referenced by `this`
13477    (function(def) {
13478        // scope of arrow function cannot bind to any context
13479        def(AST_Arrow, return_false);
13480        def(AST_AsyncArrow, return_false);
13481        def(AST_Node, function() {
13482            var result = false;
13483            var self = this;
13484            self.walk(new TreeWalker(function(node) {
13485                if (result) return true;
13486                if (node instanceof AST_This) return result = true;
13487                if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
13488            }));
13489            return result;
13490        });
13491    })(function(node, func) {
13492        node.DEFMETHOD("contains_this", func);
13493    });
13494
13495    function can_hoist_property(prop) {
13496        return prop instanceof AST_ObjectKeyVal
13497            && typeof prop.key == "string"
13498            && !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
13499    }
13500
13501    AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
13502        if (!compressor.option("properties")) return;
13503        if (key === "__proto__") return;
13504        var self = this;
13505        var expr = self.expression;
13506        if (!(expr instanceof AST_Object)) return;
13507        var props = expr.properties;
13508        for (var i = props.length; --i >= 0;) {
13509            var prop = props[i];
13510            if (prop.key !== key) continue;
13511            if (!all(props, can_hoist_property)) return;
13512            if (!safe_to_flatten(prop.value, compressor)) return;
13513            var call, scope, values = [];
13514            for (var j = 0; j < props.length; j++) {
13515                var value = props[j].value;
13516                if (props[j] instanceof AST_ObjectMethod) {
13517                    var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
13518                    if (arrow) {
13519                        if (!scope) scope = compressor.find_parent(AST_Scope);
13520                        var avoid = avoid_await_yield(compressor, scope);
13521                        value.each_argname(function(argname) {
13522                            if (avoid[argname.name]) arrow = false;
13523                        });
13524                    }
13525                    var ctor;
13526                    if (arrow) {
13527                        ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow;
13528                    } else if (i != j
13529                        || (call = compressor.parent()) instanceof AST_Call && call.expression === self) {
13530                        ctor = value.CTOR;
13531                    } else {
13532                        return;
13533                    }
13534                    value = make_node(ctor, value);
13535                }
13536                values.push(value);
13537            }
13538            return make_node(AST_Sub, self, {
13539                expression: make_node(AST_Array, expr, { elements: values }),
13540                property: make_node(AST_Number, self, { value: i }),
13541            });
13542        }
13543    });
13544
13545    OPT(AST_Dot, function(self, compressor) {
13546        if (self.property == "arguments" || self.property == "caller") {
13547            AST_Node.warn("Function.prototype.{property} not supported [{start}]", self);
13548        }
13549        var parent = compressor.parent();
13550        if (is_lhs(compressor.self(), parent)) return self;
13551        var terminated = trim_optional_chain(self, compressor);
13552        if (terminated) return terminated;
13553        if (compressor.option("sequences")
13554            && parent.TYPE != "Call"
13555            && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
13556            var seq = lift_sequence_in_expression(self, compressor);
13557            if (seq !== self) return seq.optimize(compressor);
13558        }
13559        if (compressor.option("unsafe_proto")
13560            && self.expression instanceof AST_Dot
13561            && self.expression.property == "prototype") {
13562            var exp = self.expression.expression;
13563            if (is_undeclared_ref(exp)) switch (exp.name) {
13564              case "Array":
13565                self.expression = make_node(AST_Array, self.expression, { elements: [] });
13566                break;
13567              case "Function":
13568                self.expression = make_node(AST_Function, self.expression, {
13569                    argnames: [],
13570                    body: [],
13571                }).init_vars(exp.scope);
13572                break;
13573              case "Number":
13574                self.expression = make_node(AST_Number, self.expression, { value: 0 });
13575                break;
13576              case "Object":
13577                self.expression = make_node(AST_Object, self.expression, { properties: [] });
13578                break;
13579              case "RegExp":
13580                self.expression = make_node(AST_RegExp, self.expression, { value: /t/ });
13581                break;
13582              case "String":
13583                self.expression = make_node(AST_String, self.expression, { value: "" });
13584                break;
13585            }
13586        }
13587        var sub = self.flatten_object(self.property, compressor);
13588        if (sub) return sub.optimize(compressor);
13589        return try_evaluate(compressor, self);
13590    });
13591
13592    OPT(AST_DestructuredArray, function(self, compressor) {
13593        if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) {
13594            return make_node(AST_DestructuredArray, self, {
13595                elements: self.elements.concat(self.rest.elements),
13596                rest: self.rest.rest,
13597            });
13598        }
13599        return self;
13600    });
13601
13602    OPT(AST_DestructuredKeyVal, function(self, compressor) {
13603        if (compressor.option("objects")) {
13604            var key = self.key;
13605            if (key instanceof AST_Node) {
13606                key = key.evaluate(compressor);
13607                if (key !== self.key) self.key = "" + key;
13608            }
13609        }
13610        return self;
13611    });
13612
13613    OPT(AST_Object, function(self, compressor) {
13614        if (!compressor.option("objects")) return self;
13615        var changed = false;
13616        var found = false;
13617        var generated = false;
13618        var keep_duplicate = compressor.has_directive("use strict");
13619        var keys = [];
13620        var map = new Dictionary();
13621        var values = [];
13622        self.properties.forEach(function(prop) {
13623            if (!(prop instanceof AST_Spread)) return process(prop);
13624            found = true;
13625            var exp = prop.expression;
13626            if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
13627                if (prop instanceof AST_ObjectGetter) return false;
13628                if (prop instanceof AST_Spread) return false;
13629                if (prop.key !== "__proto__") return true;
13630                if (prop instanceof AST_ObjectSetter) return true;
13631                return !prop.value.has_side_effects(compressor);
13632            })) {
13633                changed = true;
13634                exp.properties.forEach(function(prop) {
13635                    var key = prop.key;
13636                    var setter = prop instanceof AST_ObjectSetter;
13637                    if (key === "__proto__") {
13638                        if (!setter) return;
13639                        key = make_node_from_constant(key, prop);
13640                    }
13641                    process(setter ? make_node(AST_ObjectKeyVal, prop, {
13642                        key: key,
13643                        value: make_node(AST_Undefined, prop).optimize(compressor),
13644                    }) : prop);
13645                });
13646            } else {
13647                generated = true;
13648                flush();
13649                values.push(prop);
13650            }
13651        });
13652        flush();
13653        if (!changed) return self;
13654        if (found && generated && values.length == 1) {
13655            var value = values[0];
13656            if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
13657                value.key = "" + value.key.value;
13658            }
13659        }
13660        return make_node(AST_Object, self, { properties: values });
13661
13662        function flush() {
13663            keys.forEach(function(key) {
13664                var props = map.get(key);
13665                switch (props.length) {
13666                  case 0:
13667                    return;
13668                  case 1:
13669                    return values.push(props[0]);
13670                }
13671                changed = true;
13672                var tail = keep_duplicate && !generated && props.pop();
13673                values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
13674                    key: props[0].key,
13675                    value: make_sequence(self, props.map(function(prop) {
13676                        return prop.value;
13677                    })),
13678                }));
13679                if (tail) values.push(tail);
13680                props.length = 0;
13681            });
13682            keys = [];
13683            map = new Dictionary();
13684        }
13685
13686        function process(prop) {
13687            var key = prop.key;
13688            if (key instanceof AST_Node) {
13689                found = true;
13690                key = key.evaluate(compressor);
13691                if (key === prop.key || key === "__proto__") {
13692                    generated = true;
13693                } else {
13694                    key = prop.key = "" + key;
13695                }
13696            }
13697            if (can_hoist_property(prop)) {
13698                if (prop.value.has_side_effects(compressor)) flush();
13699                keys.push(key);
13700                map.add(key, prop);
13701            } else {
13702                flush();
13703                values.push(prop);
13704            }
13705            if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
13706                generated = true;
13707                if (map.has(key)) prop = map.get(key)[0];
13708                prop.key = make_node(AST_Number, prop, { value: +key });
13709            }
13710        }
13711    });
13712
13713    function flatten_var(name) {
13714        var redef = name.definition().redefined();
13715        if (redef) {
13716            name = name.clone();
13717            name.thedef = redef;
13718        }
13719        return name;
13720    }
13721
13722    function has_arg_refs(fn, node) {
13723        var found = false;
13724        node.walk(new TreeWalker(function(node) {
13725            if (found) return true;
13726            if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
13727                return found = true;
13728            }
13729        }));
13730        return found;
13731    }
13732
13733    function insert_assign(def, assign) {
13734        var visited = [];
13735        def.references.forEach(function(ref) {
13736            var fixed = ref.fixed;
13737            if (!fixed || !push_uniq(visited, fixed)) return;
13738            if (fixed.assigns) {
13739                fixed.assigns.unshift(assign);
13740            } else {
13741                fixed.assigns = [ assign ];
13742            }
13743        });
13744    }
13745
13746    function init_ref(compressor, name) {
13747        var sym = make_node(AST_SymbolRef, name);
13748        var assign = make_node(AST_Assign, name, {
13749            operator: "=",
13750            left: sym,
13751            right: make_node(AST_Undefined, name).transform(compressor),
13752        });
13753        var def = name.definition();
13754        if (def.fixed) {
13755            sym.fixed = function() {
13756                return assign.right;
13757            };
13758            sym.fixed.assigns = [ assign ];
13759            insert_assign(def, assign);
13760        }
13761        def.assignments++;
13762        def.references.push(sym);
13763        return assign;
13764    }
13765
13766    (function(def) {
13767        def(AST_Node, noop);
13768        def(AST_Assign, noop);
13769        def(AST_Await, function(compressor, scope, no_return, in_loop) {
13770            if (!compressor.option("awaits")) return;
13771            var self = this;
13772            var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, true);
13773            if (!inlined) return;
13774            if (!no_return) scan_local_returns(inlined, function(node) {
13775                node.in_bool = false;
13776                var value = node.value;
13777                if (value instanceof AST_Await) return;
13778                node.value = make_node(AST_Await, self, {
13779                    expression: value || make_node(AST_Undefined, node).transform(compressor),
13780                });
13781            });
13782            return aborts(inlined) ? inlined : make_node(AST_BlockStatement, self, {
13783                body: [ inlined, make_node(AST_SimpleStatement, self, {
13784                    body: make_node(AST_Await, self, { expression: make_node(AST_Number, self, { value: 0 })}),
13785                }) ],
13786            });
13787        });
13788        def(AST_Binary, function(compressor, scope, no_return, in_loop, in_await) {
13789            if (no_return === undefined) return;
13790            var self = this;
13791            var op = self.operator;
13792            if (!lazy_op[op]) return;
13793            var inlined = self.right.try_inline(compressor, scope, no_return, in_loop, in_await);
13794            if (!inlined) return;
13795            return make_node(AST_If, self, {
13796                condition: make_condition(self.left),
13797                body: inlined,
13798                alternative: no_return ? null : make_node(AST_Return, self, {
13799                    value: make_node(AST_Undefined, self).transform(compressor),
13800                }),
13801            });
13802
13803            function make_condition(cond) {
13804                switch (op) {
13805                  case "&&":
13806                    return cond;
13807                  case "||":
13808                    return cond.negate(compressor);
13809                  case "??":
13810                    return make_node(AST_Binary, self, {
13811                        operator: "==",
13812                        left: make_node(AST_Null, self),
13813                        right: cond,
13814                    });
13815                }
13816            }
13817        });
13818        def(AST_BlockStatement, function(compressor, scope, no_return, in_loop) {
13819            if (no_return) return;
13820            if (!this.variables) return;
13821            var body = this.body;
13822            var last = body.length - 1;
13823            if (last < 0) return;
13824            var inlined = body[last].try_inline(compressor, this, no_return, in_loop);
13825            if (!inlined) return;
13826            body[last] = inlined;
13827            return this;
13828        });
13829        def(AST_Call, function(compressor, scope, no_return, in_loop, in_await) {
13830            if (compressor.option("inline") < 4) return;
13831            var call = this;
13832            if (call.is_expr_pure(compressor)) return;
13833            var fn = call.expression;
13834            if (!(fn instanceof AST_LambdaExpression)) return;
13835            if (fn.name) return;
13836            if (fn.uses_arguments) return;
13837            if (fn.pinned()) return;
13838            if (is_generator(fn)) return;
13839            var arrow = is_arrow(fn);
13840            if (arrow && fn.value) return;
13841            if (fn.body[0] instanceof AST_Directive) return;
13842            if (fn.contains_this()) return;
13843            if (!scope) scope = find_scope(compressor);
13844            var defined = new Dictionary();
13845            defined.set("NaN", true);
13846            while (!(scope instanceof AST_Scope)) {
13847                scope.variables.each(function(def) {
13848                    defined.set(def.name, true);
13849                });
13850                scope = scope.parent_scope;
13851            }
13852            if (!member(scope, compressor.stack)) return;
13853            if (scope.pinned() && fn.variables.size() > (arrow ? 0 : 1)) return;
13854            if (scope instanceof AST_Toplevel) {
13855                if (fn.variables.size() > (arrow ? 0 : 1)) {
13856                    if (!compressor.toplevel.vars) return;
13857                    if (fn.functions.size() > 0 && !compressor.toplevel.funcs) return;
13858                }
13859                defined.set("arguments", true);
13860            }
13861            var async = !in_await && is_async(fn);
13862            if (async) {
13863                if (!compressor.option("awaits")) return;
13864                if (!is_async(scope)) return;
13865                if (call.may_throw(compressor)) return;
13866            }
13867            var names = scope.var_names();
13868            if (in_loop) in_loop = [];
13869            if (!fn.variables.all(function(def, name) {
13870                if (in_loop) in_loop.push(def);
13871                if (!defined.has(name) && !names.has(name)) return true;
13872                return !arrow && name == "arguments" && def.orig.length == 1;
13873            })) return;
13874            if (in_loop && in_loop.length > 0 && is_reachable(fn, in_loop)) return;
13875            var simple_argnames = true;
13876            if (!all(fn.argnames, function(argname) {
13877                var abort = false;
13878                var tw = new TreeWalker(function(node) {
13879                    if (abort) return true;
13880                    if (node instanceof AST_DefaultValue) {
13881                        if (has_arg_refs(fn, node.value)) return abort = true;
13882                        node.name.walk(tw);
13883                        return true;
13884                    }
13885                    if (node instanceof AST_DestructuredKeyVal) {
13886                        if (node.key instanceof AST_Node && has_arg_refs(fn, node.key)) return abort = true;
13887                        node.value.walk(tw);
13888                        return true;
13889                    }
13890                    if (node instanceof AST_SymbolFunarg && !all(node.definition().orig, function(sym) {
13891                        return !(sym instanceof AST_SymbolDefun);
13892                    })) return abort = true;
13893                });
13894                argname.walk(tw);
13895                if (abort) return false;
13896                if (!(argname instanceof AST_SymbolFunarg)) simple_argnames = false;
13897                return true;
13898            })) return;
13899            if (fn.rest) {
13900                if (has_arg_refs(fn, fn.rest)) return;
13901                simple_argnames = false;
13902            }
13903            var verify_body;
13904            if (no_return) {
13905                verify_body = function(stat) {
13906                    var abort = false;
13907                    stat.walk(new TreeWalker(function(node) {
13908                        if (abort) return true;
13909                        if (async && (node instanceof AST_Await || node instanceof AST_ForAwaitOf)
13910                            || node instanceof AST_Return) {
13911                            return abort = true;
13912                        }
13913                        if (node instanceof AST_Scope) return true;
13914                    }));
13915                    return !abort;
13916                };
13917            } else if (in_await || is_async(fn) || in_async_generator(scope)) {
13918                verify_body = function(stat) {
13919                    var abort = false;
13920                    var find_return = new TreeWalker(function(node) {
13921                        if (abort) return true;
13922                        if (node instanceof AST_Return) return abort = true;
13923                        if (node instanceof AST_Scope) return true;
13924                    });
13925                    stat.walk(new TreeWalker(function(node) {
13926                        if (abort) return true;
13927                        if (node instanceof AST_Try) {
13928                            if (node.bfinally && all(node.body, function(stat) {
13929                                stat.walk(find_return);
13930                                return !abort;
13931                            }) && node.bcatch) node.bcatch.walk(find_return);
13932                            return true;
13933                        }
13934                        if (node instanceof AST_Scope) return true;
13935                    }));
13936                    return !abort;
13937                };
13938            }
13939            if (verify_body && !all(fn.body, verify_body)) return;
13940            if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return;
13941            fn.functions.each(function(def, name) {
13942                scope.functions.set(name, def);
13943            });
13944            var body = [];
13945            fn.variables.each(function(def, name) {
13946                if (!arrow && name == "arguments" && def.orig.length == 1) return;
13947                names.set(name, true);
13948                scope.enclosed.push(def);
13949                scope.variables.set(name, def);
13950                def.single_use = false;
13951                if (!in_loop) return;
13952                if (def.references.length == def.replaced) return;
13953                if (def.orig.length == def.eliminated) return;
13954                if (def.orig.length == 1 && fn.functions.has(name)) return;
13955                if (!all(def.orig, function(sym) {
13956                    if (sym instanceof AST_SymbolConst) return false;
13957                    if (sym instanceof AST_SymbolFunarg) return !sym.unused && def.scope.resolve() !== fn;
13958                    if (sym instanceof AST_SymbolLet) return false;
13959                    return true;
13960                })) return;
13961                var sym = def.orig[0];
13962                if (sym instanceof AST_SymbolCatch) return;
13963                body.push(make_node(AST_SimpleStatement, sym, { body: init_ref(compressor, flatten_var(sym)) }));
13964            });
13965            var defs = Object.create(null), syms = new Dictionary();
13966            if (simple_argnames && all(call.args, function(arg) {
13967                return !(arg instanceof AST_Spread);
13968            })) {
13969                var values = call.args.slice();
13970                fn.argnames.forEach(function(argname) {
13971                    var value = values.shift();
13972                    if (argname.unused) {
13973                        if (value) body.push(make_node(AST_SimpleStatement, call, { body: value }));
13974                        return;
13975                    }
13976                    var defn = make_node(AST_VarDef, call, {
13977                        name: argname.convert_symbol(AST_SymbolVar, process),
13978                        value: value || make_node(AST_Undefined, call).transform(compressor),
13979                    });
13980                    if (argname instanceof AST_SymbolFunarg) insert_assign(argname.definition(), defn);
13981                    body.push(make_node(AST_Var, call, { definitions: [ defn ] }));
13982                });
13983                if (values.length) body.push(make_node(AST_SimpleStatement, call, {
13984                    body: make_sequence(call, values),
13985                }));
13986            } else {
13987                body.push(make_node(AST_Var, call, {
13988                    definitions: [ make_node(AST_VarDef, call, {
13989                        name: make_node(AST_DestructuredArray, call, {
13990                            elements: fn.argnames.map(function(argname) {
13991                                if (argname.unused) return make_node(AST_Hole, argname);
13992                                return argname.convert_symbol(AST_SymbolVar, process);
13993                            }),
13994                            rest: fn.rest && fn.rest.convert_symbol(AST_SymbolVar, process),
13995                        }),
13996                        value: make_node(AST_Array, call, { elements: call.args.slice() }),
13997                    }) ],
13998                }));
13999            }
14000            syms.each(function(orig, id) {
14001                var def = defs[id];
14002                [].unshift.apply(def.orig, orig);
14003                def.eliminated += orig.length;
14004            });
14005            [].push.apply(body, in_loop ? fn.body.filter(function(stat) {
14006                if (!(stat instanceof AST_LambdaDefinition)) return true;
14007                var name = make_node(AST_SymbolVar, flatten_var(stat.name));
14008                var def = name.definition();
14009                def.fixed = false;
14010                def.orig.push(name);
14011                def.eliminated++;
14012                body.push(make_node(AST_Var, stat, {
14013                    definitions: [ make_node(AST_VarDef, stat, {
14014                        name: name,
14015                        value: to_func_expr(stat, true),
14016                    }) ],
14017                }));
14018                return false;
14019            }) : fn.body);
14020            var inlined = make_node(AST_BlockStatement, call, { body: body });
14021            if (!no_return) {
14022                if (async) scan_local_returns(inlined, function(node) {
14023                    var value = node.value;
14024                    if (is_undefined(value)) return;
14025                    node.value = make_node(AST_Await, call, { expression: value });
14026                });
14027                body.push(make_node(AST_Return, call, {
14028                    value: in_async_generator(scope) ? make_node(AST_Undefined, call).transform(compressor) : null,
14029                }));
14030            }
14031            return inlined;
14032
14033            function process(sym, argname) {
14034                var def = argname.definition();
14035                defs[def.id] = def;
14036                syms.add(def.id, sym);
14037            }
14038        });
14039        def(AST_Conditional, function(compressor, scope, no_return, in_loop, in_await) {
14040            var self = this;
14041            var body = self.consequent.try_inline(compressor, scope, no_return, in_loop, in_await);
14042            var alt = self.alternative.try_inline(compressor, scope, no_return, in_loop, in_await);
14043            if (!body && !alt) return;
14044            return make_node(AST_If, self, {
14045                condition: self.condition,
14046                body: body || make_body(self.consequent),
14047                alternative: alt || make_body(self.alternative),
14048            });
14049
14050            function make_body(value) {
14051                if (no_return) return make_node(AST_SimpleStatement, value, { body: value });
14052                return make_node(AST_Return, value, { value: value });
14053            }
14054        });
14055        def(AST_For, function(compressor, scope, no_return, in_loop) {
14056            var body = this.body.try_inline(compressor, scope, true, true);
14057            if (body) this.body = body;
14058            var inlined = this.init;
14059            if (inlined) {
14060                inlined = inlined.try_inline(compressor, scope, true, in_loop);
14061                if (inlined) {
14062                    this.init = null;
14063                    if (inlined instanceof AST_BlockStatement) {
14064                        inlined.body.push(this);
14065                        return inlined;
14066                    }
14067                    return make_node(AST_BlockStatement, inlined, { body: [ inlined, this ] });
14068                }
14069            }
14070            return body && this;
14071        });
14072        def(AST_ForEnumeration, function(compressor, scope, no_return, in_loop) {
14073            var body = this.body.try_inline(compressor, scope, true, true);
14074            if (body) this.body = body;
14075            var obj = this.object;
14076            if (obj instanceof AST_Sequence) {
14077                var inlined = inline_sequence(compressor, scope, true, in_loop, false, obj, 1);
14078                if (inlined) {
14079                    this.object = obj.tail_node();
14080                    inlined.body.push(this);
14081                    return inlined;
14082                }
14083            }
14084            return body && this;
14085        });
14086        def(AST_If, function(compressor, scope, no_return, in_loop) {
14087            var body = this.body.try_inline(compressor, scope, no_return, in_loop);
14088            if (body) this.body = body;
14089            var alt = this.alternative;
14090            if (alt) {
14091                alt = alt.try_inline(compressor, scope, no_return, in_loop);
14092                if (alt) this.alternative = alt;
14093            }
14094            var cond = this.condition;
14095            if (cond instanceof AST_Sequence) {
14096                var inlined = inline_sequence(compressor, scope, true, in_loop, false, cond, 1);
14097                if (inlined) {
14098                    this.condition = cond.tail_node();
14099                    inlined.body.push(this);
14100                    return inlined;
14101                }
14102            }
14103            return (body || alt) && this;
14104        });
14105        def(AST_IterationStatement, function(compressor, scope, no_return, in_loop) {
14106            var body = this.body.try_inline(compressor, scope, true, true);
14107            if (!body) return;
14108            this.body = body;
14109            return this;
14110        });
14111        def(AST_LabeledStatement, function(compressor, scope, no_return, in_loop) {
14112            var body = this.body.try_inline(compressor, scope, no_return, in_loop);
14113            if (!body) return;
14114            if (this.body instanceof AST_IterationStatement && body instanceof AST_BlockStatement) {
14115                var loop = body.body.pop();
14116                this.body = loop;
14117                body.body.push(this);
14118                return body;
14119            }
14120            this.body = body;
14121            return this;
14122        });
14123        def(AST_New, noop);
14124        def(AST_Return, function(compressor, scope, no_return, in_loop) {
14125            var value = this.value;
14126            return value && value.try_inline(compressor, scope, undefined, in_loop === "try");
14127        });
14128        function inline_sequence(compressor, scope, no_return, in_loop, in_await, node, skip) {
14129            var body = [], exprs = node.expressions, no_ret = no_return;
14130            for (var i = exprs.length - (skip || 0), j = i; --i >= 0; no_ret = true, in_await = false) {
14131                var inlined = exprs[i].try_inline(compressor, scope, no_ret, in_loop, in_await);
14132                if (!inlined) continue;
14133                flush();
14134                body.push(inlined);
14135            }
14136            if (body.length == 0) return;
14137            flush();
14138            if (!no_return && body[0] instanceof AST_SimpleStatement) {
14139                body[0] = make_node(AST_Return, node, { value: body[0].body });
14140            }
14141            return make_node(AST_BlockStatement, node, { body: body.reverse() });
14142
14143            function flush() {
14144                if (j > i + 1) body.push(make_node(AST_SimpleStatement, node, {
14145                    body: make_sequence(node, exprs.slice(i + 1, j)),
14146                }));
14147                j = i;
14148            }
14149        }
14150        def(AST_Sequence, function(compressor, scope, no_return, in_loop, in_await) {
14151            return inline_sequence(compressor, scope, no_return, in_loop, in_await, this);
14152        });
14153        def(AST_SimpleStatement, function(compressor, scope, no_return, in_loop) {
14154            var body = this.body;
14155            while (body instanceof AST_UnaryPrefix) {
14156                var op = body.operator;
14157                if (unary_side_effects[op]) break;
14158                if (op == "void") break;
14159                body = body.expression;
14160            }
14161            if (!no_return && !is_undefined(body)) body = make_node(AST_UnaryPrefix, this, {
14162                operator: "void",
14163                expression: body,
14164            });
14165            return body.try_inline(compressor, scope, no_return || false, in_loop);
14166        });
14167        def(AST_UnaryPrefix, function(compressor, scope, no_return, in_loop, in_await) {
14168            var self = this;
14169            var op = self.operator;
14170            if (unary_side_effects[op]) return;
14171            if (!no_return && op == "void") no_return = false;
14172            var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, in_await);
14173            if (!inlined) return;
14174            if (!no_return) scan_local_returns(inlined, function(node) {
14175                node.in_bool = false;
14176                var value = node.value;
14177                if (op == "void" && is_undefined(value)) return;
14178                node.value = make_node(AST_UnaryPrefix, self, {
14179                    operator: op,
14180                    expression: value || make_node(AST_Undefined, node).transform(compressor),
14181                });
14182            });
14183            return inlined;
14184        });
14185        def(AST_With, function(compressor, scope, no_return, in_loop) {
14186            var body = this.body.try_inline(compressor, scope, no_return, in_loop);
14187            if (body) this.body = body;
14188            var exp = this.expression;
14189            if (exp instanceof AST_Sequence) {
14190                var inlined = inline_sequence(compressor, scope, true, in_loop, false, exp, 1);
14191                if (inlined) {
14192                    this.expression = exp.tail_node();
14193                    inlined.body.push(this);
14194                    return inlined;
14195                }
14196            }
14197            return body && this;
14198        });
14199        def(AST_Yield, function(compressor, scope, no_return, in_loop) {
14200            if (!compressor.option("yields")) return;
14201            if (!this.nested) return;
14202            var call = this.expression;
14203            if (call.TYPE != "Call") return;
14204            var fn = call.expression;
14205            switch (fn.CTOR) {
14206              case AST_AsyncGeneratorFunction:
14207                fn = make_node(AST_AsyncFunction, fn);
14208                break;
14209              case AST_GeneratorFunction:
14210                fn = make_node(AST_Function, fn);
14211                break;
14212              default:
14213                return;
14214            }
14215            call = call.clone();
14216            call.expression = fn;
14217            return call.try_inline(compressor, scope, no_return, in_loop);
14218        });
14219    })(function(node, func) {
14220        node.DEFMETHOD("try_inline", func);
14221    });
14222
14223    OPT(AST_Return, function(self, compressor) {
14224        var value = self.value;
14225        if (value && compressor.option("side_effects")
14226            && is_undefined(value, compressor)
14227            && !in_async_generator(compressor.find_parent(AST_Scope))) {
14228            self.value = null;
14229        }
14230        return self;
14231    });
14232})(function(node, optimizer) {
14233    node.DEFMETHOD("optimize", function(compressor) {
14234        var self = this;
14235        if (self._optimized) return self;
14236        if (compressor.has_directive("use asm")) return self;
14237        var opt = optimizer(self, compressor);
14238        opt._optimized = true;
14239        return opt;
14240    });
14241});
14242