1/***********************************************************************
2
3  A JavaScript tokenizer / parser / beautifier / compressor.
4  https://github.com/mishoo/UglifyJS2
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        angular       : false,
52        booleans      : !false_by_default,
53        cascade       : !false_by_default,
54        collapse_vars : !false_by_default,
55        comparisons   : !false_by_default,
56        conditionals  : !false_by_default,
57        dead_code     : !false_by_default,
58        drop_console  : false,
59        drop_debugger : !false_by_default,
60        evaluate      : !false_by_default,
61        expression    : false,
62        global_defs   : {},
63        hoist_funs    : !false_by_default,
64        hoist_vars    : false,
65        if_return     : !false_by_default,
66        join_vars     : !false_by_default,
67        keep_fargs    : true,
68        keep_fnames   : false,
69        keep_infinity : false,
70        loops         : !false_by_default,
71        negate_iife   : !false_by_default,
72        passes        : 1,
73        properties    : !false_by_default,
74        pure_getters  : !false_by_default && "strict",
75        pure_funcs    : null,
76        reduce_vars   : !false_by_default,
77        screw_ie8     : true,
78        sequences     : !false_by_default,
79        side_effects  : !false_by_default,
80        switches      : !false_by_default,
81        top_retain    : null,
82        toplevel      : !!(options && options["top_retain"]),
83        unsafe        : false,
84        unsafe_comps  : false,
85        unsafe_math   : false,
86        unsafe_proto  : false,
87        unsafe_regexp : false,
88        unused        : !false_by_default,
89        warnings      : true,
90    }, true);
91    var pure_funcs = this.options["pure_funcs"];
92    if (typeof pure_funcs == "function") {
93        this.pure_funcs = pure_funcs;
94    } else {
95        this.pure_funcs = pure_funcs ? function(node) {
96            return pure_funcs.indexOf(node.expression.print_to_string()) < 0;
97        } : return_true;
98    }
99    var top_retain = this.options["top_retain"];
100    if (top_retain instanceof RegExp) {
101        this.top_retain = function(def) {
102            return top_retain.test(def.name);
103        };
104    } else if (typeof top_retain == "function") {
105        this.top_retain = top_retain;
106    } else if (top_retain) {
107        if (typeof top_retain == "string") {
108            top_retain = top_retain.split(/,/);
109        }
110        this.top_retain = function(def) {
111            return top_retain.indexOf(def.name) >= 0;
112        };
113    }
114    var sequences = this.options["sequences"];
115    this.sequences_limit = sequences == 1 ? 200 : sequences | 0;
116    this.warnings_produced = {};
117};
118
119Compressor.prototype = new TreeTransformer;
120merge(Compressor.prototype, {
121    option: function(key) { return this.options[key] },
122    compress: function(node) {
123        if (this.option("expression")) {
124            node = node.process_expression(true);
125        }
126        var passes = +this.options.passes || 1;
127        for (var pass = 0; pass < passes && pass < 3; ++pass) {
128            if (pass > 0 || this.option("reduce_vars"))
129                node.reset_opt_flags(this, true);
130            node = node.transform(this);
131        }
132        if (this.option("expression")) {
133            node = node.process_expression(false);
134        }
135        return node;
136    },
137    info: function() {
138        if (this.options.warnings == "verbose") {
139            AST_Node.warn.apply(AST_Node, arguments);
140        }
141    },
142    warn: function(text, props) {
143        if (this.options.warnings) {
144            // only emit unique warnings
145            var message = string_template(text, props);
146            if (!(message in this.warnings_produced)) {
147                this.warnings_produced[message] = true;
148                AST_Node.warn.apply(AST_Node, arguments);
149            }
150        }
151    },
152    clear_warnings: function() {
153        this.warnings_produced = {};
154    },
155    before: function(node, descend, in_list) {
156        if (node._squeezed) return node;
157        var was_scope = false;
158        if (node instanceof AST_Scope) {
159            node = node.hoist_declarations(this);
160            was_scope = true;
161        }
162        // Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
163        // would call AST_Node.transform() if a different instance of AST_Node is
164        // produced after OPT().
165        // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
166        // Migrate and defer all children's AST_Node.transform() to below, which
167        // will now happen after this parent AST_Node has been properly substituted
168        // thus gives a consistent AST snapshot.
169        descend(node, this);
170        // Existing code relies on how AST_Node.optimize() worked, and omitting the
171        // following replacement call would result in degraded efficiency of both
172        // output and performance.
173        descend(node, this);
174        var opt = node.optimize(this);
175        if (was_scope && opt instanceof AST_Scope) {
176            opt.drop_unused(this);
177            descend(opt, this);
178        }
179        if (opt === node) opt._squeezed = true;
180        return opt;
181    }
182});
183
184(function(){
185
186    function OPT(node, optimizer) {
187        node.DEFMETHOD("optimize", function(compressor){
188            var self = this;
189            if (self._optimized) return self;
190            if (compressor.has_directive("use asm")) return self;
191            var opt = optimizer(self, compressor);
192            opt._optimized = true;
193            return opt;
194        });
195    };
196
197    OPT(AST_Node, function(self, compressor){
198        return self;
199    });
200
201    AST_Node.DEFMETHOD("equivalent_to", function(node){
202        return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
203    });
204
205    AST_Node.DEFMETHOD("process_expression", function(insert, compressor) {
206        var self = this;
207        var tt = new TreeTransformer(function(node) {
208            if (insert && node instanceof AST_SimpleStatement) {
209                return make_node(AST_Return, node, {
210                    value: node.body
211                });
212            }
213            if (!insert && node instanceof AST_Return) {
214                if (compressor) {
215                    var value = node.value && node.value.drop_side_effect_free(compressor, true);
216                    return value ? make_node(AST_SimpleStatement, node, {
217                        body: value
218                    }) : make_node(AST_EmptyStatement, node);
219                }
220                return make_node(AST_SimpleStatement, node, {
221                    body: node.value || make_node(AST_UnaryPrefix, node, {
222                        operator: "void",
223                        expression: make_node(AST_Number, node, {
224                            value: 0
225                        })
226                    })
227                });
228            }
229            if (node instanceof AST_Lambda && node !== self) {
230                return node;
231            }
232            if (node instanceof AST_Block) {
233                var index = node.body.length - 1;
234                if (index >= 0) {
235                    node.body[index] = node.body[index].transform(tt);
236                }
237            }
238            if (node instanceof AST_If) {
239                node.body = node.body.transform(tt);
240                if (node.alternative) {
241                    node.alternative = node.alternative.transform(tt);
242                }
243            }
244            if (node instanceof AST_With) {
245                node.body = node.body.transform(tt);
246            }
247            return node;
248        });
249        return self.transform(tt);
250    });
251
252    AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
253        var reduce_vars = rescan && compressor.option("reduce_vars");
254        var toplevel = compressor.option("toplevel");
255        var safe_ids = Object.create(null);
256        var suppressor = new TreeWalker(function(node) {
257            if (node instanceof AST_Symbol) {
258                var d = node.definition();
259                if (node instanceof AST_SymbolRef) d.references.push(node);
260                d.fixed = false;
261            }
262        });
263        var tw = new TreeWalker(function(node, descend){
264            node._squeezed = false;
265            node._optimized = false;
266            if (reduce_vars) {
267                if (node instanceof AST_Toplevel) node.globals.each(reset_def);
268                if (node instanceof AST_Scope) node.variables.each(reset_def);
269                if (node instanceof AST_SymbolRef) {
270                    var d = node.definition();
271                    d.references.push(node);
272                    if (d.fixed === undefined || !is_safe(d)
273                        || is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) {
274                        d.fixed = false;
275                    } else {
276                        var parent = tw.parent();
277                        if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
278                            || parent instanceof AST_Call && node !== parent.expression
279                            || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
280                            || parent instanceof AST_VarDef && node === parent.value) {
281                            d.escaped = true;
282                        }
283                    }
284                }
285                if (node instanceof AST_SymbolCatch) {
286                    node.definition().fixed = false;
287                }
288                if (node instanceof AST_VarDef) {
289                    var d = node.name.definition();
290                    if (d.fixed == null) {
291                        if (node.value) {
292                            d.fixed = function() {
293                                return node.value;
294                            };
295                            mark(d, false);
296                            descend();
297                        } else {
298                            d.fixed = null;
299                        }
300                        mark(d, true);
301                        return true;
302                    } else if (node.value) {
303                        d.fixed = false;
304                    }
305                }
306                if (node instanceof AST_Defun) {
307                    var d = node.name.definition();
308                    if (!toplevel && d.global || is_safe(d)) {
309                        d.fixed = false;
310                    } else {
311                        d.fixed = node;
312                        mark(d, true);
313                    }
314                    var save_ids = safe_ids;
315                    safe_ids = Object.create(null);
316                    descend();
317                    safe_ids = save_ids;
318                    return true;
319                }
320                if (node instanceof AST_Function) {
321                    push();
322                    var iife;
323                    if (!node.name
324                        && (iife = tw.parent()) instanceof AST_Call
325                        && iife.expression === node) {
326                        // Virtually turn IIFE parameters into variable definitions:
327                        //   (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
328                        // So existing transformation rules can work on them.
329                        node.argnames.forEach(function(arg, i) {
330                            var d = arg.definition();
331                            if (!node.uses_arguments && d.fixed === undefined) {
332                                d.fixed = function() {
333                                    return iife.args[i] || make_node(AST_Undefined, iife);
334                                };
335                                mark(d, true);
336                            } else {
337                                d.fixed = false;
338                            }
339                        });
340                    }
341                    descend();
342                    pop();
343                    return true;
344                }
345                if (node instanceof AST_Accessor) {
346                    var save_ids = safe_ids;
347                    safe_ids = Object.create(null);
348                    descend();
349                    safe_ids = save_ids;
350                    return true;
351                }
352                if (node instanceof AST_Binary
353                    && (node.operator == "&&" || node.operator == "||")) {
354                    node.left.walk(tw);
355                    push();
356                    node.right.walk(tw);
357                    pop();
358                    return true;
359                }
360                if (node instanceof AST_Conditional) {
361                    node.condition.walk(tw);
362                    push();
363                    node.consequent.walk(tw);
364                    pop();
365                    push();
366                    node.alternative.walk(tw);
367                    pop();
368                    return true;
369                }
370                if (node instanceof AST_If || node instanceof AST_DWLoop) {
371                    node.condition.walk(tw);
372                    push();
373                    node.body.walk(tw);
374                    pop();
375                    if (node.alternative) {
376                        push();
377                        node.alternative.walk(tw);
378                        pop();
379                    }
380                    return true;
381                }
382                if (node instanceof AST_LabeledStatement) {
383                    push();
384                    node.body.walk(tw);
385                    pop();
386                    return true;
387                }
388                if (node instanceof AST_For) {
389                    if (node.init) node.init.walk(tw);
390                    push();
391                    if (node.condition) node.condition.walk(tw);
392                    node.body.walk(tw);
393                    if (node.step) node.step.walk(tw);
394                    pop();
395                    return true;
396                }
397                if (node instanceof AST_ForIn) {
398                    node.init.walk(suppressor);
399                    node.object.walk(tw);
400                    push();
401                    node.body.walk(tw);
402                    pop();
403                    return true;
404                }
405                if (node instanceof AST_Try) {
406                    push();
407                    walk_body(node, tw);
408                    pop();
409                    if (node.bcatch) {
410                        push();
411                        node.bcatch.walk(tw);
412                        pop();
413                    }
414                    if (node.bfinally) node.bfinally.walk(tw);
415                    return true;
416                }
417                if (node instanceof AST_SwitchBranch) {
418                    push();
419                    descend();
420                    pop();
421                    return true;
422                }
423            }
424        });
425        this.walk(tw);
426
427        function mark(def, safe) {
428            safe_ids[def.id] = safe;
429        }
430
431        function is_safe(def) {
432            if (safe_ids[def.id]) {
433                if (def.fixed == null) {
434                    var orig = def.orig[0];
435                    if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
436                    def.fixed = make_node(AST_Undefined, orig);
437                }
438                return true;
439            }
440        }
441
442        function push() {
443            safe_ids = Object.create(safe_ids);
444        }
445
446        function pop() {
447            safe_ids = Object.getPrototypeOf(safe_ids);
448        }
449
450        function reset_def(def) {
451            def.escaped = false;
452            if (def.scope.uses_eval) {
453                def.fixed = false;
454            } else if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) {
455                def.fixed = undefined;
456            } else {
457                def.fixed = false;
458            }
459            def.references = [];
460            def.should_replace = undefined;
461        }
462
463        function is_modified(node, level, func) {
464            var parent = tw.parent(level);
465            if (is_lhs(node, parent)
466                || !func && parent instanceof AST_Call && parent.expression === node) {
467                return true;
468            } else if (parent instanceof AST_PropAccess && parent.expression === node) {
469                return !func && is_modified(parent, level + 1);
470            }
471        }
472    });
473
474    AST_SymbolRef.DEFMETHOD("fixed_value", function() {
475        var fixed = this.definition().fixed;
476        if (!fixed || fixed instanceof AST_Node) return fixed;
477        return fixed();
478    });
479
480    function is_reference_const(ref) {
481        if (!(ref instanceof AST_SymbolRef)) return false;
482        var orig = ref.definition().orig;
483        for (var i = orig.length; --i >= 0;) {
484            if (orig[i] instanceof AST_SymbolConst) return true;
485        }
486    }
487
488    function find_variable(compressor, name) {
489        var scope, i = 0;
490        while (scope = compressor.parent(i++)) {
491            if (scope instanceof AST_Scope) break;
492            if (scope instanceof AST_Catch) {
493                scope = scope.argname.definition().scope;
494                break;
495            }
496        }
497        return scope.find_variable(name);
498    }
499
500    function make_node(ctor, orig, props) {
501        if (!props) props = {};
502        if (orig) {
503            if (!props.start) props.start = orig.start;
504            if (!props.end) props.end = orig.end;
505        }
506        return new ctor(props);
507    };
508
509    function make_node_from_constant(val, orig) {
510        switch (typeof val) {
511          case "string":
512            return make_node(AST_String, orig, {
513                value: val
514            });
515          case "number":
516            if (isNaN(val)) return make_node(AST_NaN, orig);
517            if (isFinite(val)) {
518                return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
519                    operator: "-",
520                    expression: make_node(AST_Number, orig, { value: -val })
521                }) : make_node(AST_Number, orig, { value: val });
522            }
523            return val < 0 ? make_node(AST_UnaryPrefix, orig, {
524                operator: "-",
525                expression: make_node(AST_Infinity, orig)
526            }) : make_node(AST_Infinity, orig);
527          case "boolean":
528            return make_node(val ? AST_True : AST_False, orig);
529          case "undefined":
530            return make_node(AST_Undefined, orig);
531          default:
532            if (val === null) {
533                return make_node(AST_Null, orig, { value: null });
534            }
535            if (val instanceof RegExp) {
536                return make_node(AST_RegExp, orig, { value: val });
537            }
538            throw new Error(string_template("Can't handle constant of type: {type}", {
539                type: typeof val
540            }));
541        }
542    };
543
544    // we shouldn't compress (1,func)(something) to
545    // func(something) because that changes the meaning of
546    // the func (becomes lexical instead of global).
547    function maintain_this_binding(parent, orig, val) {
548        if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
549            || parent instanceof AST_Call && parent.expression === orig
550                && (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
551            return make_node(AST_Seq, orig, {
552                car: make_node(AST_Number, orig, {
553                    value: 0
554                }),
555                cdr: val
556            });
557        }
558        return val;
559    }
560
561    function as_statement_array(thing) {
562        if (thing === null) return [];
563        if (thing instanceof AST_BlockStatement) return thing.body;
564        if (thing instanceof AST_EmptyStatement) return [];
565        if (thing instanceof AST_Statement) return [ thing ];
566        throw new Error("Can't convert thing to statement array");
567    };
568
569    function is_empty(thing) {
570        if (thing === null) return true;
571        if (thing instanceof AST_EmptyStatement) return true;
572        if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
573        return false;
574    };
575
576    function loop_body(x) {
577        if (x instanceof AST_Switch) return x;
578        if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
579            return (x.body instanceof AST_BlockStatement ? x.body : x);
580        }
581        return x;
582    };
583
584    function is_iife_call(node) {
585        if (node instanceof AST_Call && !(node instanceof AST_New)) {
586            return node.expression instanceof AST_Function || is_iife_call(node.expression);
587        }
588        return false;
589    }
590
591    function tighten_body(statements, compressor) {
592        var CHANGED, max_iter = 10;
593        do {
594            CHANGED = false;
595            if (compressor.option("angular")) {
596                statements = process_for_angular(statements);
597            }
598            statements = eliminate_spurious_blocks(statements);
599            if (compressor.option("dead_code")) {
600                statements = eliminate_dead_code(statements, compressor);
601            }
602            if (compressor.option("if_return")) {
603                statements = handle_if_return(statements, compressor);
604            }
605            if (compressor.sequences_limit > 0) {
606                statements = sequencesize(statements, compressor);
607            }
608            if (compressor.option("join_vars")) {
609                statements = join_consecutive_vars(statements, compressor);
610            }
611            if (compressor.option("collapse_vars")) {
612                statements = collapse_single_use_vars(statements, compressor);
613            }
614        } while (CHANGED && max_iter-- > 0);
615
616        return statements;
617
618        function collapse_single_use_vars(statements, compressor) {
619            // Iterate statements backwards looking for a statement with a var/const
620            // declaration immediately preceding it. Grab the rightmost var definition
621            // and if it has exactly one reference then attempt to replace its reference
622            // in the statement with the var value and then erase the var definition.
623
624            var self = compressor.self();
625            var var_defs_removed = false;
626            var toplevel = compressor.option("toplevel");
627            for (var stat_index = statements.length; --stat_index >= 0;) {
628                var stat = statements[stat_index];
629                if (stat instanceof AST_Definitions) continue;
630
631                // Process child blocks of statement if present.
632                [stat, stat.body, stat.alternative, stat.bcatch, stat.bfinally].forEach(function(node) {
633                    node && node.body && collapse_single_use_vars(node.body, compressor);
634                });
635
636                // The variable definition must precede a statement.
637                if (stat_index <= 0) break;
638                var prev_stat_index = stat_index - 1;
639                var prev_stat = statements[prev_stat_index];
640                if (!(prev_stat instanceof AST_Definitions)) continue;
641                var var_defs = prev_stat.definitions;
642                if (var_defs == null) continue;
643
644                var var_names_seen = {};
645                var side_effects_encountered = false;
646                var lvalues_encountered = false;
647                var lvalues = {};
648
649                // Scan variable definitions from right to left.
650                for (var var_defs_index = var_defs.length; --var_defs_index >= 0;) {
651
652                    // Obtain var declaration and var name with basic sanity check.
653                    var var_decl = var_defs[var_defs_index];
654                    if (var_decl.value == null) break;
655                    var var_name = var_decl.name.name;
656                    if (!var_name || !var_name.length) break;
657
658                    // Bail if we've seen a var definition of same name before.
659                    if (var_name in var_names_seen) break;
660                    var_names_seen[var_name] = true;
661
662                    // Only interested in cases with just one reference to the variable.
663                    var def = self.find_variable && self.find_variable(var_name);
664                    if (!def || !def.references || def.references.length !== 1
665                        || var_name == "arguments" || (!toplevel && def.global)) {
666                        side_effects_encountered = true;
667                        continue;
668                    }
669                    var ref = def.references[0];
670
671                    // Don't replace ref if eval() or with statement in scope.
672                    if (ref.scope.uses_eval || ref.scope.uses_with) break;
673
674                    // Constant single use vars can be replaced in any scope.
675                    if (var_decl.value.is_constant()) {
676                        var ctt = new TreeTransformer(function(node) {
677                            var parent = ctt.parent();
678                            if (parent instanceof AST_IterationStatement
679                                && (parent.condition === node || parent.init === node)) {
680                                return node;
681                            }
682                            if (node === ref)
683                                return replace_var(node, parent, true);
684                        });
685                        stat.transform(ctt);
686                        continue;
687                    }
688
689                    // Restrict var replacement to constants if side effects encountered.
690                    if (side_effects_encountered |= lvalues_encountered) continue;
691
692                    var value_has_side_effects = var_decl.value.has_side_effects(compressor);
693                    // Non-constant single use vars can only be replaced in same scope.
694                    if (ref.scope !== self) {
695                        side_effects_encountered |= value_has_side_effects;
696                        continue;
697                    }
698
699                    // Detect lvalues in var value.
700                    var tw = new TreeWalker(function(node){
701                        if (node instanceof AST_SymbolRef && is_lvalue(node, tw.parent())) {
702                            lvalues[node.name] = lvalues_encountered = true;
703                        }
704                    });
705                    var_decl.value.walk(tw);
706
707                    // Replace the non-constant single use var in statement if side effect free.
708                    var unwind = false;
709                    var tt = new TreeTransformer(
710                        function preorder(node) {
711                            if (unwind) return node;
712                            var parent = tt.parent();
713                            if (node instanceof AST_Lambda
714                                || node instanceof AST_Try
715                                || node instanceof AST_With
716                                || node instanceof AST_Case
717                                || node instanceof AST_IterationStatement
718                                || (parent instanceof AST_If          && node !== parent.condition)
719                                || (parent instanceof AST_Conditional && node !== parent.condition)
720                                || (node instanceof AST_SymbolRef
721                                    && value_has_side_effects
722                                    && !are_references_in_scope(node.definition(), self))
723                                || (parent instanceof AST_Binary
724                                    && (parent.operator == "&&" || parent.operator == "||")
725                                    && node === parent.right)
726                                || (parent instanceof AST_Switch && node !== parent.expression)) {
727                                return side_effects_encountered = unwind = true, node;
728                            }
729                            function are_references_in_scope(def, scope) {
730                                if (def.orig.length === 1
731                                    && def.orig[0] instanceof AST_SymbolDefun) return true;
732                                if (def.scope !== scope) return false;
733                                var refs = def.references;
734                                for (var i = 0, len = refs.length; i < len; i++) {
735                                    if (refs[i].scope !== scope) return false;
736                                }
737                                return true;
738                            }
739                        },
740                        function postorder(node) {
741                            if (unwind) return node;
742                            if (node === ref)
743                                return unwind = true, replace_var(node, tt.parent(), false);
744                            if (side_effects_encountered |= node.has_side_effects(compressor))
745                                return unwind = true, node;
746                            if (lvalues_encountered && node instanceof AST_SymbolRef && node.name in lvalues) {
747                                side_effects_encountered = true;
748                                return unwind = true, node;
749                            }
750                        }
751                    );
752                    stat.transform(tt);
753                }
754            }
755
756            // Remove extraneous empty statments in block after removing var definitions.
757            // Leave at least one statement in `statements`.
758            if (var_defs_removed) for (var i = statements.length; --i >= 0;) {
759                if (statements.length > 1 && statements[i] instanceof AST_EmptyStatement)
760                    statements.splice(i, 1);
761            }
762
763            return statements;
764
765            function is_lvalue(node, parent) {
766                return node instanceof AST_SymbolRef && is_lhs(node, parent);
767            }
768            function replace_var(node, parent, is_constant) {
769                if (is_lvalue(node, parent)) return node;
770
771                // Remove var definition and return its value to the TreeTransformer to replace.
772                var value = maintain_this_binding(parent, node, var_decl.value);
773                var_decl.value = null;
774
775                var_defs.splice(var_defs_index, 1);
776                if (var_defs.length === 0) {
777                    statements[prev_stat_index] = make_node(AST_EmptyStatement, self);
778                    var_defs_removed = true;
779                }
780                // Further optimize statement after substitution.
781                stat.reset_opt_flags(compressor);
782
783                compressor.info("Collapsing " + (is_constant ? "constant" : "variable") +
784                    " " + var_name + " [{file}:{line},{col}]", node.start);
785                CHANGED = true;
786                return value;
787            }
788        }
789
790        function process_for_angular(statements) {
791            function has_inject(comment) {
792                return /@ngInject/.test(comment.value);
793            }
794            function make_arguments_names_list(func) {
795                return func.argnames.map(function(sym){
796                    return make_node(AST_String, sym, { value: sym.name });
797                });
798            }
799            function make_array(orig, elements) {
800                return make_node(AST_Array, orig, { elements: elements });
801            }
802            function make_injector(func, name) {
803                return make_node(AST_SimpleStatement, func, {
804                    body: make_node(AST_Assign, func, {
805                        operator: "=",
806                        left: make_node(AST_Dot, name, {
807                            expression: make_node(AST_SymbolRef, name, name),
808                            property: "$inject"
809                        }),
810                        right: make_array(func, make_arguments_names_list(func))
811                    })
812                });
813            }
814            function check_expression(body) {
815                if (body && body.args) {
816                    // if this is a function call check all of arguments passed
817                    body.args.forEach(function(argument, index, array) {
818                        var comments = argument.start.comments_before;
819                        // if the argument is function preceded by @ngInject
820                        if (argument instanceof AST_Lambda && comments.length && has_inject(comments[0])) {
821                            // replace the function with an array of names of its parameters and function at the end
822                            array[index] = make_array(argument, make_arguments_names_list(argument).concat(argument));
823                        }
824                    });
825                    // if this is chained call check previous one recursively
826                    if (body.expression && body.expression.expression) {
827                        check_expression(body.expression.expression);
828                    }
829                }
830            }
831            return statements.reduce(function(a, stat){
832                a.push(stat);
833
834                if (stat.body && stat.body.args) {
835                    check_expression(stat.body);
836                } else {
837                    var token = stat.start;
838                    var comments = token.comments_before;
839                    if (comments && comments.length > 0) {
840                        var last = comments.pop();
841                        if (has_inject(last)) {
842                            // case 1: defun
843                            if (stat instanceof AST_Defun) {
844                                a.push(make_injector(stat, stat.name));
845                            }
846                            else if (stat instanceof AST_Definitions) {
847                                stat.definitions.forEach(function(def) {
848                                    if (def.value && def.value instanceof AST_Lambda) {
849                                        a.push(make_injector(def.value, def.name));
850                                    }
851                                });
852                            }
853                            else {
854                                compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token);
855                            }
856                        }
857                    }
858                }
859
860                return a;
861            }, []);
862        }
863
864        function eliminate_spurious_blocks(statements) {
865            var seen_dirs = [];
866            return statements.reduce(function(a, stat){
867                if (stat instanceof AST_BlockStatement) {
868                    CHANGED = true;
869                    a.push.apply(a, eliminate_spurious_blocks(stat.body));
870                } else if (stat instanceof AST_EmptyStatement) {
871                    CHANGED = true;
872                } else if (stat instanceof AST_Directive) {
873                    if (seen_dirs.indexOf(stat.value) < 0) {
874                        a.push(stat);
875                        seen_dirs.push(stat.value);
876                    } else {
877                        CHANGED = true;
878                    }
879                } else {
880                    a.push(stat);
881                }
882                return a;
883            }, []);
884        };
885
886        function handle_if_return(statements, compressor) {
887            var self = compressor.self();
888            var multiple_if_returns = has_multiple_if_returns(statements);
889            var in_lambda = self instanceof AST_Lambda;
890            var ret = []; // Optimized statements, build from tail to front
891            loop: for (var i = statements.length; --i >= 0;) {
892                var stat = statements[i];
893                switch (true) {
894                  case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
895                    CHANGED = true;
896                    // note, ret.length is probably always zero
897                    // because we drop unreachable code before this
898                    // step.  nevertheless, it's good to check.
899                    continue loop;
900                  case stat instanceof AST_If:
901                    if (stat.body instanceof AST_Return) {
902                        //---
903                        // pretty silly case, but:
904                        // if (foo()) return; return; ==> foo(); return;
905                        if (((in_lambda && ret.length == 0)
906                             || (ret[0] instanceof AST_Return && !ret[0].value))
907                            && !stat.body.value && !stat.alternative) {
908                            CHANGED = true;
909                            var cond = make_node(AST_SimpleStatement, stat.condition, {
910                                body: stat.condition
911                            });
912                            ret.unshift(cond);
913                            continue loop;
914                        }
915                        //---
916                        // if (foo()) return x; return y; ==> return foo() ? x : y;
917                        if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
918                            CHANGED = true;
919                            stat = stat.clone();
920                            stat.alternative = ret[0];
921                            ret[0] = stat.transform(compressor);
922                            continue loop;
923                        }
924                        //---
925                        // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
926                        if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return)
927                            && stat.body.value && !stat.alternative && in_lambda) {
928                            CHANGED = true;
929                            stat = stat.clone();
930                            stat.alternative = ret[0] || make_node(AST_Return, stat, {
931                                value: null
932                            });
933                            ret[0] = stat.transform(compressor);
934                            continue loop;
935                        }
936                        //---
937                        // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
938                        if (!stat.body.value && in_lambda) {
939                            CHANGED = true;
940                            stat = stat.clone();
941                            stat.condition = stat.condition.negate(compressor);
942                            var body = as_statement_array(stat.alternative).concat(ret);
943                            var funs = extract_functions_from_statement_array(body);
944                            stat.body = make_node(AST_BlockStatement, stat, {
945                                body: body
946                            });
947                            stat.alternative = null;
948                            ret = funs.concat([ stat.transform(compressor) ]);
949                            continue loop;
950                        }
951
952                        //---
953                        // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
954                        //
955                        // if sequences is not enabled, this can lead to an endless loop (issue #866).
956                        // however, with sequences on this helps producing slightly better output for
957                        // the example code.
958                        if (compressor.option("sequences")
959                            && i > 0 && statements[i - 1] instanceof AST_If && statements[i - 1].body instanceof AST_Return
960                            && ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
961                            && !stat.alternative) {
962                            CHANGED = true;
963                            ret.push(make_node(AST_Return, ret[0], {
964                                value: null
965                            }).transform(compressor));
966                            ret.unshift(stat);
967                            continue loop;
968                        }
969                    }
970
971                    var ab = aborts(stat.body);
972                    var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
973                    if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
974                               || (ab instanceof AST_Continue && self === loop_body(lct))
975                               || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
976                        if (ab.label) {
977                            remove(ab.label.thedef.references, ab);
978                        }
979                        CHANGED = true;
980                        var body = as_statement_array(stat.body).slice(0, -1);
981                        stat = stat.clone();
982                        stat.condition = stat.condition.negate(compressor);
983                        stat.body = make_node(AST_BlockStatement, stat, {
984                            body: as_statement_array(stat.alternative).concat(ret)
985                        });
986                        stat.alternative = make_node(AST_BlockStatement, stat, {
987                            body: body
988                        });
989                        ret = [ stat.transform(compressor) ];
990                        continue loop;
991                    }
992
993                    var ab = aborts(stat.alternative);
994                    var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
995                    if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
996                               || (ab instanceof AST_Continue && self === loop_body(lct))
997                               || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
998                        if (ab.label) {
999                            remove(ab.label.thedef.references, ab);
1000                        }
1001                        CHANGED = true;
1002                        stat = stat.clone();
1003                        stat.body = make_node(AST_BlockStatement, stat.body, {
1004                            body: as_statement_array(stat.body).concat(ret)
1005                        });
1006                        stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
1007                            body: as_statement_array(stat.alternative).slice(0, -1)
1008                        });
1009                        ret = [ stat.transform(compressor) ];
1010                        continue loop;
1011                    }
1012
1013                    ret.unshift(stat);
1014                    break;
1015                  default:
1016                    ret.unshift(stat);
1017                    break;
1018                }
1019            }
1020            return ret;
1021
1022            function has_multiple_if_returns(statements) {
1023                var n = 0;
1024                for (var i = statements.length; --i >= 0;) {
1025                    var stat = statements[i];
1026                    if (stat instanceof AST_If && stat.body instanceof AST_Return) {
1027                        if (++n > 1) return true;
1028                    }
1029                }
1030                return false;
1031            }
1032        };
1033
1034        function eliminate_dead_code(statements, compressor) {
1035            var has_quit = false;
1036            var orig = statements.length;
1037            var self = compressor.self();
1038            statements = statements.reduce(function(a, stat){
1039                if (has_quit) {
1040                    extract_declarations_from_unreachable_code(compressor, stat, a);
1041                } else {
1042                    if (stat instanceof AST_LoopControl) {
1043                        var lct = compressor.loopcontrol_target(stat);
1044                        if ((stat instanceof AST_Break
1045                             && !(lct instanceof AST_IterationStatement)
1046                             && loop_body(lct) === self) || (stat instanceof AST_Continue
1047                                                             && loop_body(lct) === self)) {
1048                            if (stat.label) {
1049                                remove(stat.label.thedef.references, stat);
1050                            }
1051                        } else {
1052                            a.push(stat);
1053                        }
1054                    } else {
1055                        a.push(stat);
1056                    }
1057                    if (aborts(stat)) has_quit = true;
1058                }
1059                return a;
1060            }, []);
1061            CHANGED = statements.length != orig;
1062            return statements;
1063        };
1064
1065        function sequencesize(statements, compressor) {
1066            if (statements.length < 2) return statements;
1067            var seq = [], ret = [];
1068            function push_seq() {
1069                seq = AST_Seq.from_array(seq);
1070                if (seq) ret.push(make_node(AST_SimpleStatement, seq, {
1071                    body: seq
1072                }));
1073                seq = [];
1074            };
1075            statements.forEach(function(stat){
1076                if (stat instanceof AST_SimpleStatement) {
1077                    if (seqLength(seq) >= compressor.sequences_limit) push_seq();
1078                    var body = stat.body;
1079                    if (seq.length > 0) body = body.drop_side_effect_free(compressor);
1080                    if (body) seq.push(body);
1081                } else {
1082                    push_seq();
1083                    ret.push(stat);
1084                }
1085            });
1086            push_seq();
1087            ret = sequencesize_2(ret, compressor);
1088            CHANGED = ret.length != statements.length;
1089            return ret;
1090        };
1091
1092        function seqLength(a) {
1093            for (var len = 0, i = 0; i < a.length; ++i) {
1094                var stat = a[i];
1095                if (stat instanceof AST_Seq) {
1096                    len += stat.len();
1097                } else {
1098                    len++;
1099                }
1100            }
1101            return len;
1102        };
1103
1104        function sequencesize_2(statements, compressor) {
1105            function cons_seq(right) {
1106                ret.pop();
1107                var left = prev.body;
1108                if (left instanceof AST_Seq) {
1109                    left.add(right);
1110                } else {
1111                    left = AST_Seq.cons(left, right);
1112                }
1113                return left.transform(compressor);
1114            };
1115            var ret = [], prev = null;
1116            statements.forEach(function(stat){
1117                if (prev) {
1118                    if (stat instanceof AST_For) {
1119                        var opera = {};
1120                        try {
1121                            prev.body.walk(new TreeWalker(function(node){
1122                                if (node instanceof AST_Binary && node.operator == "in")
1123                                    throw opera;
1124                            }));
1125                            if (stat.init && !(stat.init instanceof AST_Definitions)) {
1126                                stat.init = cons_seq(stat.init);
1127                            }
1128                            else if (!stat.init) {
1129                                stat.init = prev.body.drop_side_effect_free(compressor);
1130                                ret.pop();
1131                            }
1132                        } catch(ex) {
1133                            if (ex !== opera) throw ex;
1134                        }
1135                    }
1136                    else if (stat instanceof AST_If) {
1137                        stat.condition = cons_seq(stat.condition);
1138                    }
1139                    else if (stat instanceof AST_With) {
1140                        stat.expression = cons_seq(stat.expression);
1141                    }
1142                    else if (stat instanceof AST_Exit && stat.value) {
1143                        stat.value = cons_seq(stat.value);
1144                    }
1145                    else if (stat instanceof AST_Exit) {
1146                        stat.value = cons_seq(make_node(AST_Undefined, stat).transform(compressor));
1147                    }
1148                    else if (stat instanceof AST_Switch) {
1149                        stat.expression = cons_seq(stat.expression);
1150                    }
1151                }
1152                ret.push(stat);
1153                prev = stat instanceof AST_SimpleStatement ? stat : null;
1154            });
1155            return ret;
1156        };
1157
1158        function join_consecutive_vars(statements, compressor) {
1159            var prev = null;
1160            return statements.reduce(function(a, stat){
1161                if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
1162                    prev.definitions = prev.definitions.concat(stat.definitions);
1163                    CHANGED = true;
1164                }
1165                else if (stat instanceof AST_For
1166                         && prev instanceof AST_Var
1167                         && (!stat.init || stat.init.TYPE == prev.TYPE)) {
1168                    CHANGED = true;
1169                    a.pop();
1170                    if (stat.init) {
1171                        stat.init.definitions = prev.definitions.concat(stat.init.definitions);
1172                    } else {
1173                        stat.init = prev;
1174                    }
1175                    a.push(stat);
1176                    prev = stat;
1177                }
1178                else {
1179                    prev = stat;
1180                    a.push(stat);
1181                }
1182                return a;
1183            }, []);
1184        };
1185
1186    };
1187
1188    function extract_functions_from_statement_array(statements) {
1189        var funs = [];
1190        for (var i = statements.length - 1; i >= 0; --i) {
1191            var stat = statements[i];
1192            if (stat instanceof AST_Defun) {
1193                statements.splice(i, 1);
1194                funs.unshift(stat);
1195            }
1196        }
1197        return funs;
1198    }
1199
1200    function extract_declarations_from_unreachable_code(compressor, stat, target) {
1201        if (!(stat instanceof AST_Defun)) {
1202            compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
1203        }
1204        stat.walk(new TreeWalker(function(node){
1205            if (node instanceof AST_Definitions) {
1206                compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
1207                node.remove_initializers();
1208                target.push(node);
1209                return true;
1210            }
1211            if (node instanceof AST_Defun) {
1212                target.push(node);
1213                return true;
1214            }
1215            if (node instanceof AST_Scope) {
1216                return true;
1217            }
1218        }));
1219    };
1220
1221    function is_undefined(node, compressor) {
1222        return node.is_undefined
1223            || node instanceof AST_Undefined
1224            || node instanceof AST_UnaryPrefix
1225                && node.operator == "void"
1226                && !node.expression.has_side_effects(compressor);
1227    }
1228
1229    // may_throw_on_access()
1230    // returns true if this node may be null, undefined or contain `AST_Accessor`
1231    (function(def) {
1232        AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
1233            var pure_getters = compressor.option("pure_getters");
1234            return !pure_getters || this._throw_on_access(pure_getters);
1235        });
1236
1237        function is_strict(pure_getters) {
1238            return /strict/.test(pure_getters);
1239        }
1240
1241        def(AST_Node, is_strict);
1242        def(AST_Null, return_true);
1243        def(AST_Undefined, return_true);
1244        def(AST_Constant, return_false);
1245        def(AST_Array, return_false);
1246        def(AST_Object, function(pure_getters) {
1247            if (!is_strict(pure_getters)) return false;
1248            for (var i = this.properties.length; --i >=0;)
1249                if (this.properties[i].value instanceof AST_Accessor) return true;
1250            return false;
1251        });
1252        def(AST_Function, return_false);
1253        def(AST_UnaryPostfix, return_false);
1254        def(AST_UnaryPrefix, function() {
1255            return this.operator == "void";
1256        });
1257        def(AST_Binary, function(pure_getters) {
1258            switch (this.operator) {
1259              case "&&":
1260                return this.left._throw_on_access(pure_getters);
1261              case "||":
1262                return this.left._throw_on_access(pure_getters)
1263                    && this.right._throw_on_access(pure_getters);
1264              default:
1265                return false;
1266            }
1267        })
1268        def(AST_Assign, function(pure_getters) {
1269            return this.operator == "="
1270                && this.right._throw_on_access(pure_getters);
1271        })
1272        def(AST_Conditional, function(pure_getters) {
1273            return this.consequent._throw_on_access(pure_getters)
1274                || this.alternative._throw_on_access(pure_getters);
1275        })
1276        def(AST_Seq, function(pure_getters) {
1277            return this.cdr._throw_on_access(pure_getters);
1278        });
1279        def(AST_SymbolRef, function(pure_getters) {
1280            if (this.is_undefined) return true;
1281            if (!is_strict(pure_getters)) return false;
1282            var fixed = this.fixed_value();
1283            return !fixed || fixed._throw_on_access(pure_getters);
1284        });
1285    })(function(node, func) {
1286        node.DEFMETHOD("_throw_on_access", func);
1287    });
1288
1289    /* -----[ boolean/negation helpers ]----- */
1290
1291    // methods to determine whether an expression has a boolean result type
1292    (function (def){
1293        var unary_bool = [ "!", "delete" ];
1294        var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ];
1295        def(AST_Node, return_false);
1296        def(AST_UnaryPrefix, function(){
1297            return member(this.operator, unary_bool);
1298        });
1299        def(AST_Binary, function(){
1300            return member(this.operator, binary_bool) ||
1301                ( (this.operator == "&&" || this.operator == "||") &&
1302                  this.left.is_boolean() && this.right.is_boolean() );
1303        });
1304        def(AST_Conditional, function(){
1305            return this.consequent.is_boolean() && this.alternative.is_boolean();
1306        });
1307        def(AST_Assign, function(){
1308            return this.operator == "=" && this.right.is_boolean();
1309        });
1310        def(AST_Seq, function(){
1311            return this.cdr.is_boolean();
1312        });
1313        def(AST_True, return_true);
1314        def(AST_False, return_true);
1315    })(function(node, func){
1316        node.DEFMETHOD("is_boolean", func);
1317    });
1318
1319    // methods to determine if an expression has a numeric result type
1320    (function (def){
1321        def(AST_Node, return_false);
1322        def(AST_Number, return_true);
1323        var unary = makePredicate("+ - ~ ++ --");
1324        def(AST_Unary, function(){
1325            return unary(this.operator);
1326        });
1327        var binary = makePredicate("- * / % & | ^ << >> >>>");
1328        def(AST_Binary, function(compressor){
1329            return binary(this.operator) || this.operator == "+"
1330                && this.left.is_number(compressor)
1331                && this.right.is_number(compressor);
1332        });
1333        def(AST_Assign, function(compressor){
1334            return binary(this.operator.slice(0, -1))
1335                || this.operator == "=" && this.right.is_number(compressor);
1336        });
1337        def(AST_Seq, function(compressor){
1338            return this.cdr.is_number(compressor);
1339        });
1340        def(AST_Conditional, function(compressor){
1341            return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
1342        });
1343    })(function(node, func){
1344        node.DEFMETHOD("is_number", func);
1345    });
1346
1347    // methods to determine if an expression has a string result type
1348    (function (def){
1349        def(AST_Node, return_false);
1350        def(AST_String, return_true);
1351        def(AST_UnaryPrefix, function(){
1352            return this.operator == "typeof";
1353        });
1354        def(AST_Binary, function(compressor){
1355            return this.operator == "+" &&
1356                (this.left.is_string(compressor) || this.right.is_string(compressor));
1357        });
1358        def(AST_Assign, function(compressor){
1359            return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
1360        });
1361        def(AST_Seq, function(compressor){
1362            return this.cdr.is_string(compressor);
1363        });
1364        def(AST_Conditional, function(compressor){
1365            return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
1366        });
1367    })(function(node, func){
1368        node.DEFMETHOD("is_string", func);
1369    });
1370
1371    var unary_side_effects = makePredicate("delete ++ --");
1372
1373    function is_lhs(node, parent) {
1374        if (parent instanceof AST_Unary && unary_side_effects(parent.operator)) return parent.expression;
1375        if (parent instanceof AST_Assign && parent.left === node) return node;
1376    }
1377
1378    (function (def){
1379        AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
1380            if (!compressor.option("global_defs")) return;
1381            var def = this._find_defs(compressor, "");
1382            if (def) {
1383                var node, parent = this, level = 0;
1384                do {
1385                    node = parent;
1386                    parent = compressor.parent(level++);
1387                } while (parent instanceof AST_PropAccess && parent.expression === node);
1388                if (is_lhs(node, parent)) {
1389                    compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start);
1390                } else {
1391                    return def;
1392                }
1393            }
1394        });
1395        function to_node(value, orig) {
1396            if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
1397            if (Array.isArray(value)) return make_node(AST_Array, orig, {
1398                elements: value.map(function(value) {
1399                    return to_node(value, orig);
1400                })
1401            });
1402            if (value && typeof value == "object") {
1403                var props = [];
1404                for (var key in value) {
1405                    props.push(make_node(AST_ObjectKeyVal, orig, {
1406                        key: key,
1407                        value: to_node(value[key], orig)
1408                    }));
1409                }
1410                return make_node(AST_Object, orig, {
1411                    properties: props
1412                });
1413            }
1414            return make_node_from_constant(value, orig);
1415        }
1416        def(AST_Node, noop);
1417        def(AST_Dot, function(compressor, suffix){
1418            return this.expression._find_defs(compressor, "." + this.property + suffix);
1419        });
1420        def(AST_SymbolRef, function(compressor, suffix){
1421            if (!this.global()) return;
1422            var name;
1423            var defines = compressor.option("global_defs");
1424            if (defines && HOP(defines, (name = this.name + suffix))) {
1425                var node = to_node(defines[name], this);
1426                var top = compressor.find_parent(AST_Toplevel);
1427                node.walk(new TreeWalker(function(node) {
1428                    if (node instanceof AST_SymbolRef) {
1429                        node.scope = top;
1430                        node.thedef = top.def_global(node);
1431                    }
1432                }));
1433                return node;
1434            }
1435        });
1436    })(function(node, func){
1437        node.DEFMETHOD("_find_defs", func);
1438    });
1439
1440    function best_of_expression(ast1, ast2) {
1441        return ast1.print_to_string().length >
1442            ast2.print_to_string().length
1443            ? ast2 : ast1;
1444    }
1445
1446    function best_of_statement(ast1, ast2) {
1447        return best_of_expression(make_node(AST_SimpleStatement, ast1, {
1448            body: ast1
1449        }), make_node(AST_SimpleStatement, ast2, {
1450            body: ast2
1451        })).body;
1452    }
1453
1454    function best_of(compressor, ast1, ast2) {
1455        return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
1456    }
1457
1458    // methods to evaluate a constant expression
1459    (function (def){
1460        // If the node has been successfully reduced to a constant,
1461        // then its value is returned; otherwise the element itself
1462        // is returned.
1463        // They can be distinguished as constant value is never a
1464        // descendant of AST_Node.
1465        AST_Node.DEFMETHOD("evaluate", function(compressor){
1466            if (!compressor.option("evaluate")) return this;
1467            try {
1468                var val = this._eval(compressor);
1469                return !val || val instanceof RegExp || typeof val != "object" ? val : this;
1470            } catch(ex) {
1471                if (ex !== def) throw ex;
1472                return this;
1473            }
1474        });
1475        var unaryPrefix = makePredicate("! ~ - + void");
1476        AST_Node.DEFMETHOD("is_constant", function(){
1477            // Accomodate when compress option evaluate=false
1478            // as well as the common constant expressions !0 and -1
1479            if (this instanceof AST_Constant) {
1480                return !(this instanceof AST_RegExp);
1481            } else {
1482                return this instanceof AST_UnaryPrefix
1483                    && this.expression instanceof AST_Constant
1484                    && unaryPrefix(this.operator);
1485            }
1486        });
1487        // Obtain the constant value of an expression already known to be constant.
1488        // Result only valid iff this.is_constant() is true.
1489        AST_Node.DEFMETHOD("constant_value", function(compressor){
1490            // Accomodate when option evaluate=false.
1491            if (this instanceof AST_Constant && !(this instanceof AST_RegExp)) {
1492                return this.value;
1493            }
1494            // Accomodate the common constant expressions !0 and -1 when option evaluate=false.
1495            if (this instanceof AST_UnaryPrefix
1496                && this.expression instanceof AST_Constant) switch (this.operator) {
1497              case "!":
1498                return !this.expression.value;
1499              case "~":
1500                return ~this.expression.value;
1501              case "-":
1502                return -this.expression.value;
1503              case "+":
1504                return +this.expression.value;
1505              default:
1506                throw new Error(string_template("Cannot evaluate unary expression {value}", {
1507                    value: this.print_to_string()
1508                }));
1509            }
1510            var result = this.evaluate(compressor);
1511            if (result !== this) {
1512                return result;
1513            }
1514            throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
1515        });
1516        def(AST_Statement, function(){
1517            throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
1518        });
1519        def(AST_Lambda, function(){
1520            throw def;
1521        });
1522        function ev(node, compressor) {
1523            if (!compressor) throw new Error("Compressor must be passed");
1524
1525            return node._eval(compressor);
1526        };
1527        def(AST_Node, function(){
1528            throw def;          // not constant
1529        });
1530        def(AST_Constant, function(){
1531            return this.getValue();
1532        });
1533        def(AST_Array, function(compressor){
1534            if (compressor.option("unsafe")) {
1535                return this.elements.map(function(element) {
1536                    return ev(element, compressor);
1537                });
1538            }
1539            throw def;
1540        });
1541        def(AST_Object, function(compressor){
1542            if (compressor.option("unsafe")) {
1543                var val = {};
1544                for (var i = 0, len = this.properties.length; i < len; i++) {
1545                    var prop = this.properties[i];
1546                    var key = prop.key;
1547                    if (key instanceof AST_Symbol) {
1548                        key = key.name;
1549                    } else if (key instanceof AST_Node) {
1550                        key = ev(key, compressor);
1551                    }
1552                    if (typeof Object.prototype[key] === 'function') {
1553                        throw def;
1554                    }
1555                    val[key] = ev(prop.value, compressor);
1556                }
1557                return val;
1558            }
1559            throw def;
1560        });
1561        def(AST_UnaryPrefix, function(compressor){
1562            var e = this.expression;
1563            switch (this.operator) {
1564              case "!": return !ev(e, compressor);
1565              case "typeof":
1566                // Function would be evaluated to an array and so typeof would
1567                // incorrectly return 'object'. Hence making is a special case.
1568                if (e instanceof AST_Function) return typeof function(){};
1569
1570                e = ev(e, compressor);
1571
1572                // typeof <RegExp> returns "object" or "function" on different platforms
1573                // so cannot evaluate reliably
1574                if (e instanceof RegExp) throw def;
1575
1576                return typeof e;
1577              case "void": return void ev(e, compressor);
1578              case "~": return ~ev(e, compressor);
1579              case "-": return -ev(e, compressor);
1580              case "+": return +ev(e, compressor);
1581            }
1582            throw def;
1583        });
1584        def(AST_Binary, function(c){
1585            var left = this.left, right = this.right, result;
1586            switch (this.operator) {
1587              case "&&"  : result = ev(left, c) &&  ev(right, c); break;
1588              case "||"  : result = ev(left, c) ||  ev(right, c); break;
1589              case "|"   : result = ev(left, c) |   ev(right, c); break;
1590              case "&"   : result = ev(left, c) &   ev(right, c); break;
1591              case "^"   : result = ev(left, c) ^   ev(right, c); break;
1592              case "+"   : result = ev(left, c) +   ev(right, c); break;
1593              case "*"   : result = ev(left, c) *   ev(right, c); break;
1594              case "/"   : result = ev(left, c) /   ev(right, c); break;
1595              case "%"   : result = ev(left, c) %   ev(right, c); break;
1596              case "-"   : result = ev(left, c) -   ev(right, c); break;
1597              case "<<"  : result = ev(left, c) <<  ev(right, c); break;
1598              case ">>"  : result = ev(left, c) >>  ev(right, c); break;
1599              case ">>>" : result = ev(left, c) >>> ev(right, c); break;
1600              case "=="  : result = ev(left, c) ==  ev(right, c); break;
1601              case "===" : result = ev(left, c) === ev(right, c); break;
1602              case "!="  : result = ev(left, c) !=  ev(right, c); break;
1603              case "!==" : result = ev(left, c) !== ev(right, c); break;
1604              case "<"   : result = ev(left, c) <   ev(right, c); break;
1605              case "<="  : result = ev(left, c) <=  ev(right, c); break;
1606              case ">"   : result = ev(left, c) >   ev(right, c); break;
1607              case ">="  : result = ev(left, c) >=  ev(right, c); break;
1608              default:
1609                  throw def;
1610            }
1611            if (isNaN(result) && c.find_parent(AST_With)) {
1612                // leave original expression as is
1613                throw def;
1614            }
1615            return result;
1616        });
1617        def(AST_Conditional, function(compressor){
1618            return ev(this.condition, compressor)
1619                ? ev(this.consequent, compressor)
1620                : ev(this.alternative, compressor);
1621        });
1622        def(AST_SymbolRef, function(compressor){
1623            if (!compressor.option("reduce_vars") || this._evaluating) throw def;
1624            this._evaluating = true;
1625            try {
1626                var fixed = this.fixed_value();
1627                if (!fixed) throw def;
1628                var value = ev(fixed, compressor);
1629                if (!HOP(fixed, "_eval")) fixed._eval = function() {
1630                    return value;
1631                };
1632                if (value && typeof value == "object" && this.definition().escaped) throw def;
1633                return value;
1634            } finally {
1635                this._evaluating = false;
1636            }
1637        });
1638        def(AST_PropAccess, function(compressor){
1639            if (compressor.option("unsafe")) {
1640                var key = this.property;
1641                if (key instanceof AST_Node) {
1642                    key = ev(key, compressor);
1643                }
1644                var val = ev(this.expression, compressor);
1645                if (val && HOP(val, key)) {
1646                    return val[key];
1647                }
1648            }
1649            throw def;
1650        });
1651    })(function(node, func){
1652        node.DEFMETHOD("_eval", func);
1653    });
1654
1655    // method to negate an expression
1656    (function(def){
1657        function basic_negation(exp) {
1658            return make_node(AST_UnaryPrefix, exp, {
1659                operator: "!",
1660                expression: exp
1661            });
1662        }
1663        function best(orig, alt, first_in_statement) {
1664            var negated = basic_negation(orig);
1665            if (first_in_statement) {
1666                var stat = make_node(AST_SimpleStatement, alt, {
1667                    body: alt
1668                });
1669                return best_of_expression(negated, stat) === stat ? alt : negated;
1670            }
1671            return best_of_expression(negated, alt);
1672        }
1673        def(AST_Node, function(){
1674            return basic_negation(this);
1675        });
1676        def(AST_Statement, function(){
1677            throw new Error("Cannot negate a statement");
1678        });
1679        def(AST_Function, function(){
1680            return basic_negation(this);
1681        });
1682        def(AST_UnaryPrefix, function(){
1683            if (this.operator == "!")
1684                return this.expression;
1685            return basic_negation(this);
1686        });
1687        def(AST_Seq, function(compressor){
1688            var self = this.clone();
1689            self.cdr = self.cdr.negate(compressor);
1690            return self;
1691        });
1692        def(AST_Conditional, function(compressor, first_in_statement){
1693            var self = this.clone();
1694            self.consequent = self.consequent.negate(compressor);
1695            self.alternative = self.alternative.negate(compressor);
1696            return best(this, self, first_in_statement);
1697        });
1698        def(AST_Binary, function(compressor, first_in_statement){
1699            var self = this.clone(), op = this.operator;
1700            if (compressor.option("unsafe_comps")) {
1701                switch (op) {
1702                  case "<=" : self.operator = ">"  ; return self;
1703                  case "<"  : self.operator = ">=" ; return self;
1704                  case ">=" : self.operator = "<"  ; return self;
1705                  case ">"  : self.operator = "<=" ; return self;
1706                }
1707            }
1708            switch (op) {
1709              case "==" : self.operator = "!="; return self;
1710              case "!=" : self.operator = "=="; return self;
1711              case "===": self.operator = "!=="; return self;
1712              case "!==": self.operator = "==="; return self;
1713              case "&&":
1714                self.operator = "||";
1715                self.left = self.left.negate(compressor, first_in_statement);
1716                self.right = self.right.negate(compressor);
1717                return best(this, self, first_in_statement);
1718              case "||":
1719                self.operator = "&&";
1720                self.left = self.left.negate(compressor, first_in_statement);
1721                self.right = self.right.negate(compressor);
1722                return best(this, self, first_in_statement);
1723            }
1724            return basic_negation(this);
1725        });
1726    })(function(node, func){
1727        node.DEFMETHOD("negate", function(compressor, first_in_statement){
1728            return func.call(this, compressor, first_in_statement);
1729        });
1730    });
1731
1732    AST_Call.DEFMETHOD("has_pure_annotation", function(compressor) {
1733        if (!compressor.option("side_effects")) return false;
1734        if (this.pure !== undefined) return this.pure;
1735        var pure = false;
1736        var comments, last_comment;
1737        if (this.start
1738            && (comments = this.start.comments_before)
1739            && comments.length
1740            && /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) {
1741            pure = last_comment;
1742        }
1743        return this.pure = pure;
1744    });
1745
1746    // determine if expression has side effects
1747    (function(def){
1748        def(AST_Node, return_true);
1749
1750        def(AST_EmptyStatement, return_false);
1751        def(AST_Constant, return_false);
1752        def(AST_This, return_false);
1753
1754        def(AST_Call, function(compressor){
1755            if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return true;
1756            for (var i = this.args.length; --i >= 0;) {
1757                if (this.args[i].has_side_effects(compressor))
1758                    return true;
1759            }
1760            return false;
1761        });
1762
1763        function any(list, compressor) {
1764            for (var i = list.length; --i >= 0;)
1765                if (list[i].has_side_effects(compressor))
1766                    return true;
1767            return false;
1768        }
1769
1770        def(AST_Block, function(compressor){
1771            return any(this.body, compressor);
1772        });
1773        def(AST_Switch, function(compressor){
1774            return this.expression.has_side_effects(compressor)
1775                || any(this.body, compressor);
1776        });
1777        def(AST_Case, function(compressor){
1778            return this.expression.has_side_effects(compressor)
1779                || any(this.body, compressor);
1780        });
1781        def(AST_Try, function(compressor){
1782            return any(this.body, compressor)
1783                || this.bcatch && this.bcatch.has_side_effects(compressor)
1784                || this.bfinally && this.bfinally.has_side_effects(compressor);
1785        });
1786        def(AST_If, function(compressor){
1787            return this.condition.has_side_effects(compressor)
1788                || this.body && this.body.has_side_effects(compressor)
1789                || this.alternative && this.alternative.has_side_effects(compressor);
1790        });
1791        def(AST_LabeledStatement, function(compressor){
1792            return this.body.has_side_effects(compressor);
1793        });
1794        def(AST_SimpleStatement, function(compressor){
1795            return this.body.has_side_effects(compressor);
1796        });
1797        def(AST_Defun, return_true);
1798        def(AST_Function, return_false);
1799        def(AST_Binary, function(compressor){
1800            return this.left.has_side_effects(compressor)
1801                || this.right.has_side_effects(compressor);
1802        });
1803        def(AST_Assign, return_true);
1804        def(AST_Conditional, function(compressor){
1805            return this.condition.has_side_effects(compressor)
1806                || this.consequent.has_side_effects(compressor)
1807                || this.alternative.has_side_effects(compressor);
1808        });
1809        def(AST_Unary, function(compressor){
1810            return unary_side_effects(this.operator)
1811                || this.expression.has_side_effects(compressor);
1812        });
1813        def(AST_SymbolRef, function(compressor){
1814            return this.undeclared();
1815        });
1816        def(AST_Object, function(compressor){
1817            return any(this.properties, compressor);
1818        });
1819        def(AST_ObjectProperty, function(compressor){
1820            return this.value.has_side_effects(compressor);
1821        });
1822        def(AST_Array, function(compressor){
1823            return any(this.elements, compressor);
1824        });
1825        def(AST_Dot, function(compressor){
1826            return this.expression.may_throw_on_access(compressor)
1827                || this.expression.has_side_effects(compressor);
1828        });
1829        def(AST_Sub, function(compressor){
1830            return this.expression.may_throw_on_access(compressor)
1831                || this.expression.has_side_effects(compressor)
1832                || this.property.has_side_effects(compressor);
1833        });
1834        def(AST_Seq, function(compressor){
1835            return this.car.has_side_effects(compressor)
1836                || this.cdr.has_side_effects(compressor);
1837        });
1838    })(function(node, func){
1839        node.DEFMETHOD("has_side_effects", func);
1840    });
1841
1842    // tell me if a statement aborts
1843    function aborts(thing) {
1844        return thing && thing.aborts();
1845    };
1846    (function(def){
1847        def(AST_Statement, return_null);
1848        def(AST_Jump, return_this);
1849        function block_aborts(){
1850            var n = this.body.length;
1851            return n > 0 && aborts(this.body[n - 1]);
1852        };
1853        def(AST_BlockStatement, block_aborts);
1854        def(AST_SwitchBranch, block_aborts);
1855        def(AST_If, function(){
1856            return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
1857        });
1858    })(function(node, func){
1859        node.DEFMETHOD("aborts", func);
1860    });
1861
1862    /* -----[ optimizers ]----- */
1863
1864    OPT(AST_Directive, function(self, compressor){
1865        if (compressor.has_directive(self.value) !== self) {
1866            return make_node(AST_EmptyStatement, self);
1867        }
1868        return self;
1869    });
1870
1871    OPT(AST_Debugger, function(self, compressor){
1872        if (compressor.option("drop_debugger"))
1873            return make_node(AST_EmptyStatement, self);
1874        return self;
1875    });
1876
1877    OPT(AST_LabeledStatement, function(self, compressor){
1878        if (self.body instanceof AST_Break
1879            && compressor.loopcontrol_target(self.body) === self.body) {
1880            return make_node(AST_EmptyStatement, self);
1881        }
1882        return self.label.references.length == 0 ? self.body : self;
1883    });
1884
1885    OPT(AST_Block, function(self, compressor){
1886        self.body = tighten_body(self.body, compressor);
1887        return self;
1888    });
1889
1890    OPT(AST_BlockStatement, function(self, compressor){
1891        self.body = tighten_body(self.body, compressor);
1892        switch (self.body.length) {
1893          case 1: return self.body[0];
1894          case 0: return make_node(AST_EmptyStatement, self);
1895        }
1896        return self;
1897    });
1898
1899    AST_Scope.DEFMETHOD("drop_unused", function(compressor){
1900        var self = this;
1901        if (compressor.has_directive("use asm")) return self;
1902        var toplevel = compressor.option("toplevel");
1903        if (compressor.option("unused")
1904            && (!(self instanceof AST_Toplevel) || toplevel)
1905            && !self.uses_eval
1906            && !self.uses_with) {
1907            var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
1908            var drop_funcs = /funcs/.test(toplevel);
1909            var drop_vars = /vars/.test(toplevel);
1910            if (!(self instanceof AST_Toplevel) || toplevel == true) {
1911                drop_funcs = drop_vars = true;
1912            }
1913            var in_use = [];
1914            var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
1915            if (self instanceof AST_Toplevel && compressor.top_retain) {
1916                self.variables.each(function(def) {
1917                    if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
1918                        in_use_ids[def.id] = true;
1919                        in_use.push(def);
1920                    }
1921                });
1922            }
1923            var initializations = new Dictionary();
1924            // pass 1: find out which symbols are directly used in
1925            // this scope (not in nested scopes).
1926            var scope = this;
1927            var tw = new TreeWalker(function(node, descend){
1928                if (node !== self) {
1929                    if (node instanceof AST_Defun) {
1930                        if (!drop_funcs && scope === self) {
1931                            var node_def = node.name.definition();
1932                            if (!(node_def.id in in_use_ids)) {
1933                                in_use_ids[node_def.id] = true;
1934                                in_use.push(node_def);
1935                            }
1936                        }
1937                        initializations.add(node.name.name, node);
1938                        return true; // don't go in nested scopes
1939                    }
1940                    if (node instanceof AST_Definitions && scope === self) {
1941                        node.definitions.forEach(function(def){
1942                            if (!drop_vars) {
1943                                var node_def = def.name.definition();
1944                                if (!(node_def.id in in_use_ids)) {
1945                                    in_use_ids[node_def.id] = true;
1946                                    in_use.push(node_def);
1947                                }
1948                            }
1949                            if (def.value) {
1950                                initializations.add(def.name.name, def.value);
1951                                if (def.value.has_side_effects(compressor)) {
1952                                    def.value.walk(tw);
1953                                }
1954                            }
1955                        });
1956                        return true;
1957                    }
1958                    if (assign_as_unused
1959                        && node instanceof AST_Assign
1960                        && node.operator == "="
1961                        && node.left instanceof AST_SymbolRef
1962                        && !is_reference_const(node.left)
1963                        && scope === self) {
1964                        node.right.walk(tw);
1965                        return true;
1966                    }
1967                    if (node instanceof AST_SymbolRef) {
1968                        var node_def = node.definition();
1969                        if (!(node_def.id in in_use_ids)) {
1970                            in_use_ids[node_def.id] = true;
1971                            in_use.push(node_def);
1972                        }
1973                        return true;
1974                    }
1975                    if (node instanceof AST_Scope) {
1976                        var save_scope = scope;
1977                        scope = node;
1978                        descend();
1979                        scope = save_scope;
1980                        return true;
1981                    }
1982                }
1983            });
1984            self.walk(tw);
1985            // pass 2: for every used symbol we need to walk its
1986            // initialization code to figure out if it uses other
1987            // symbols (that may not be in_use).
1988            for (var i = 0; i < in_use.length; ++i) {
1989                in_use[i].orig.forEach(function(decl){
1990                    // undeclared globals will be instanceof AST_SymbolRef
1991                    var init = initializations.get(decl.name);
1992                    if (init) init.forEach(function(init){
1993                        var tw = new TreeWalker(function(node){
1994                            if (node instanceof AST_SymbolRef) {
1995                                var node_def = node.definition();
1996                                if (!(node_def.id in in_use_ids)) {
1997                                    in_use_ids[node_def.id] = true;
1998                                    in_use.push(node_def);
1999                                }
2000                            }
2001                        });
2002                        init.walk(tw);
2003                    });
2004                });
2005            }
2006            // pass 3: we should drop declarations not in_use
2007            var tt = new TreeTransformer(
2008                function before(node, descend, in_list) {
2009                    if (node instanceof AST_Function
2010                        && node.name
2011                        && !compressor.option("keep_fnames")) {
2012                        var def = node.name.definition();
2013                        // any declarations with same name will overshadow
2014                        // name of this anonymous function and can therefore
2015                        // never be used anywhere
2016                        if (!(def.id in in_use_ids) || def.orig.length > 1)
2017                            node.name = null;
2018                    }
2019                    if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
2020                        var trim = !compressor.option("keep_fargs");
2021                        for (var a = node.argnames, i = a.length; --i >= 0;) {
2022                            var sym = a[i];
2023                            if (!(sym.definition().id in in_use_ids)) {
2024                                sym.__unused = true;
2025                                if (trim) {
2026                                    a.pop();
2027                                    compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", {
2028                                        name : sym.name,
2029                                        file : sym.start.file,
2030                                        line : sym.start.line,
2031                                        col  : sym.start.col
2032                                    });
2033                                }
2034                            }
2035                            else {
2036                                trim = false;
2037                            }
2038                        }
2039                    }
2040                    if (drop_funcs && node instanceof AST_Defun && node !== self) {
2041                        if (!(node.name.definition().id in in_use_ids)) {
2042                            compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", {
2043                                name : node.name.name,
2044                                file : node.name.start.file,
2045                                line : node.name.start.line,
2046                                col  : node.name.start.col
2047                            });
2048                            return make_node(AST_EmptyStatement, node);
2049                        }
2050                        return node;
2051                    }
2052                    if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) {
2053                        var def = node.definitions.filter(function(def){
2054                            if (def.value) def.value = def.value.transform(tt);
2055                            var sym = def.name.definition();
2056                            if (sym.id in in_use_ids) return true;
2057                            if (sym.orig[0] instanceof AST_SymbolCatch) {
2058                                def.value = def.value && def.value.drop_side_effect_free(compressor);
2059                                return true;
2060                            }
2061                            var w = {
2062                                name : def.name.name,
2063                                file : def.name.start.file,
2064                                line : def.name.start.line,
2065                                col  : def.name.start.col
2066                            };
2067                            if (def.value && (def._unused_side_effects = def.value.drop_side_effect_free(compressor))) {
2068                                compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
2069                                return true;
2070                            }
2071                            compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", w);
2072                            return false;
2073                        });
2074                        // place uninitialized names at the start
2075                        def = mergeSort(def, function(a, b){
2076                            if (!a.value && b.value) return -1;
2077                            if (!b.value && a.value) return 1;
2078                            return 0;
2079                        });
2080                        // for unused names whose initialization has
2081                        // side effects, we can cascade the init. code
2082                        // into the next one, or next statement.
2083                        var side_effects = [];
2084                        for (var i = 0; i < def.length;) {
2085                            var x = def[i];
2086                            if (x._unused_side_effects) {
2087                                side_effects.push(x._unused_side_effects);
2088                                def.splice(i, 1);
2089                            } else {
2090                                if (side_effects.length > 0) {
2091                                    side_effects.push(x.value);
2092                                    x.value = AST_Seq.from_array(side_effects);
2093                                    side_effects = [];
2094                                }
2095                                ++i;
2096                            }
2097                        }
2098                        if (side_effects.length > 0) {
2099                            side_effects = make_node(AST_BlockStatement, node, {
2100                                body: [ make_node(AST_SimpleStatement, node, {
2101                                    body: AST_Seq.from_array(side_effects)
2102                                }) ]
2103                            });
2104                        } else {
2105                            side_effects = null;
2106                        }
2107                        if (def.length == 0 && !side_effects) {
2108                            return make_node(AST_EmptyStatement, node);
2109                        }
2110                        if (def.length == 0) {
2111                            return in_list ? MAP.splice(side_effects.body) : side_effects;
2112                        }
2113                        node.definitions = def;
2114                        if (side_effects) {
2115                            side_effects.body.unshift(node);
2116                            return in_list ? MAP.splice(side_effects.body) : side_effects;
2117                        }
2118                        return node;
2119                    }
2120                    if (drop_vars && assign_as_unused
2121                        && node instanceof AST_Assign
2122                        && node.operator == "="
2123                        && node.left instanceof AST_SymbolRef) {
2124                        var def = node.left.definition();
2125                        if (!(def.id in in_use_ids)
2126                            && self.variables.get(def.name) === def) {
2127                            return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
2128                        }
2129                    }
2130                    // certain combination of unused name + side effect leads to:
2131                    //    https://github.com/mishoo/UglifyJS2/issues/44
2132                    //    https://github.com/mishoo/UglifyJS2/issues/1830
2133                    // that's an invalid AST.
2134                    // We fix it at this stage by moving the `var` outside the `for`.
2135                    if (node instanceof AST_For) {
2136                        descend(node, this);
2137                        if (node.init instanceof AST_BlockStatement) {
2138                            var block = node.init;
2139                            node.init = block.body.pop();
2140                            block.body.push(node);
2141                            return in_list ? MAP.splice(block.body) : block;
2142                        } else if (is_empty(node.init)) {
2143                            node.init = null;
2144                        }
2145                        return node;
2146                    }
2147                    if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
2148                        descend(node, this);
2149                        if (node.body instanceof AST_BlockStatement) {
2150                            var block = node.body;
2151                            node.body = block.body.pop();
2152                            block.body.push(node);
2153                            return in_list ? MAP.splice(block.body) : block;
2154                        }
2155                        return node;
2156                    }
2157                    if (node instanceof AST_Scope && node !== self)
2158                        return node;
2159                }
2160            );
2161            self.transform(tt);
2162        }
2163    });
2164
2165    AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
2166        var self = this;
2167        if (compressor.has_directive("use asm")) return self;
2168        var hoist_funs = compressor.option("hoist_funs");
2169        var hoist_vars = compressor.option("hoist_vars");
2170        if (hoist_funs || hoist_vars) {
2171            var dirs = [];
2172            var hoisted = [];
2173            var vars = new Dictionary(), vars_found = 0, var_decl = 0;
2174            // let's count var_decl first, we seem to waste a lot of
2175            // space if we hoist `var` when there's only one.
2176            self.walk(new TreeWalker(function(node){
2177                if (node instanceof AST_Scope && node !== self)
2178                    return true;
2179                if (node instanceof AST_Var) {
2180                    ++var_decl;
2181                    return true;
2182                }
2183            }));
2184            hoist_vars = hoist_vars && var_decl > 1;
2185            var tt = new TreeTransformer(
2186                function before(node) {
2187                    if (node !== self) {
2188                        if (node instanceof AST_Directive) {
2189                            dirs.push(node);
2190                            return make_node(AST_EmptyStatement, node);
2191                        }
2192                        if (node instanceof AST_Defun && hoist_funs) {
2193                            hoisted.push(node);
2194                            return make_node(AST_EmptyStatement, node);
2195                        }
2196                        if (node instanceof AST_Var && hoist_vars) {
2197                            node.definitions.forEach(function(def){
2198                                vars.set(def.name.name, def);
2199                                ++vars_found;
2200                            });
2201                            var seq = node.to_assignments(compressor);
2202                            var p = tt.parent();
2203                            if (p instanceof AST_ForIn && p.init === node) {
2204                                if (seq == null) {
2205                                    var def = node.definitions[0].name;
2206                                    return make_node(AST_SymbolRef, def, def);
2207                                }
2208                                return seq;
2209                            }
2210                            if (p instanceof AST_For && p.init === node) {
2211                                return seq;
2212                            }
2213                            if (!seq) return make_node(AST_EmptyStatement, node);
2214                            return make_node(AST_SimpleStatement, node, {
2215                                body: seq
2216                            });
2217                        }
2218                        if (node instanceof AST_Scope)
2219                            return node; // to avoid descending in nested scopes
2220                    }
2221                }
2222            );
2223            self = self.transform(tt);
2224            if (vars_found > 0) {
2225                // collect only vars which don't show up in self's arguments list
2226                var defs = [];
2227                vars.each(function(def, name){
2228                    if (self instanceof AST_Lambda
2229                        && find_if(function(x){ return x.name == def.name.name },
2230                                   self.argnames)) {
2231                        vars.del(name);
2232                    } else {
2233                        def = def.clone();
2234                        def.value = null;
2235                        defs.push(def);
2236                        vars.set(name, def);
2237                    }
2238                });
2239                if (defs.length > 0) {
2240                    // try to merge in assignments
2241                    for (var i = 0; i < self.body.length;) {
2242                        if (self.body[i] instanceof AST_SimpleStatement) {
2243                            var expr = self.body[i].body, sym, assign;
2244                            if (expr instanceof AST_Assign
2245                                && expr.operator == "="
2246                                && (sym = expr.left) instanceof AST_Symbol
2247                                && vars.has(sym.name))
2248                            {
2249                                var def = vars.get(sym.name);
2250                                if (def.value) break;
2251                                def.value = expr.right;
2252                                remove(defs, def);
2253                                defs.push(def);
2254                                self.body.splice(i, 1);
2255                                continue;
2256                            }
2257                            if (expr instanceof AST_Seq
2258                                && (assign = expr.car) instanceof AST_Assign
2259                                && assign.operator == "="
2260                                && (sym = assign.left) instanceof AST_Symbol
2261                                && vars.has(sym.name))
2262                            {
2263                                var def = vars.get(sym.name);
2264                                if (def.value) break;
2265                                def.value = assign.right;
2266                                remove(defs, def);
2267                                defs.push(def);
2268                                self.body[i].body = expr.cdr;
2269                                continue;
2270                            }
2271                        }
2272                        if (self.body[i] instanceof AST_EmptyStatement) {
2273                            self.body.splice(i, 1);
2274                            continue;
2275                        }
2276                        if (self.body[i] instanceof AST_BlockStatement) {
2277                            var tmp = [ i, 1 ].concat(self.body[i].body);
2278                            self.body.splice.apply(self.body, tmp);
2279                            continue;
2280                        }
2281                        break;
2282                    }
2283                    defs = make_node(AST_Var, self, {
2284                        definitions: defs
2285                    });
2286                    hoisted.push(defs);
2287                };
2288            }
2289            self.body = dirs.concat(hoisted, self.body);
2290        }
2291        return self;
2292    });
2293
2294    // drop_side_effect_free()
2295    // remove side-effect-free parts which only affects return value
2296    (function(def){
2297        // Drop side-effect-free elements from an array of expressions.
2298        // Returns an array of expressions with side-effects or null
2299        // if all elements were dropped. Note: original array may be
2300        // returned if nothing changed.
2301        function trim(nodes, compressor, first_in_statement) {
2302            var ret = [], changed = false;
2303            for (var i = 0, len = nodes.length; i < len; i++) {
2304                var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
2305                changed |= node !== nodes[i];
2306                if (node) {
2307                    ret.push(node);
2308                    first_in_statement = false;
2309                }
2310            }
2311            return changed ? ret.length ? ret : null : nodes;
2312        }
2313
2314        def(AST_Node, return_this);
2315        def(AST_Constant, return_null);
2316        def(AST_This, return_null);
2317        def(AST_Call, function(compressor, first_in_statement){
2318            if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) {
2319                if (this.expression instanceof AST_Function
2320                    && (!this.expression.name || !this.expression.name.definition().references.length)) {
2321                    var node = this.clone();
2322                    node.expression = node.expression.process_expression(false, compressor);
2323                    return node;
2324                }
2325                return this;
2326            }
2327            if (this.pure) {
2328                compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
2329                this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
2330            }
2331            var args = trim(this.args, compressor, first_in_statement);
2332            return args && AST_Seq.from_array(args);
2333        });
2334        def(AST_Accessor, return_null);
2335        def(AST_Function, return_null);
2336        def(AST_Binary, function(compressor, first_in_statement){
2337            var right = this.right.drop_side_effect_free(compressor);
2338            if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
2339            switch (this.operator) {
2340              case "&&":
2341              case "||":
2342                if (right === this.right) return this;
2343                var node = this.clone();
2344                node.right = right;
2345                return node;
2346              default:
2347                var left = this.left.drop_side_effect_free(compressor, first_in_statement);
2348                if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement);
2349                return make_node(AST_Seq, this, {
2350                    car: left,
2351                    cdr: right
2352                });
2353            }
2354        });
2355        def(AST_Assign, return_this);
2356        def(AST_Conditional, function(compressor){
2357            var consequent = this.consequent.drop_side_effect_free(compressor);
2358            var alternative = this.alternative.drop_side_effect_free(compressor);
2359            if (consequent === this.consequent && alternative === this.alternative) return this;
2360            if (!consequent) return alternative ? make_node(AST_Binary, this, {
2361                operator: "||",
2362                left: this.condition,
2363                right: alternative
2364            }) : this.condition.drop_side_effect_free(compressor);
2365            if (!alternative) return make_node(AST_Binary, this, {
2366                operator: "&&",
2367                left: this.condition,
2368                right: consequent
2369            });
2370            var node = this.clone();
2371            node.consequent = consequent;
2372            node.alternative = alternative;
2373            return node;
2374        });
2375        def(AST_Unary, function(compressor, first_in_statement){
2376            if (unary_side_effects(this.operator)) return this;
2377            if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) return null;
2378            var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
2379            if (first_in_statement
2380                && this instanceof AST_UnaryPrefix
2381                && is_iife_call(expression)) {
2382                if (expression === this.expression && this.operator.length === 1) return this;
2383                return make_node(AST_UnaryPrefix, this, {
2384                    operator: this.operator.length === 1 ? this.operator : "!",
2385                    expression: expression
2386                });
2387            }
2388            return expression;
2389        });
2390        def(AST_SymbolRef, function() {
2391            return this.undeclared() ? this : null;
2392        });
2393        def(AST_Object, function(compressor, first_in_statement){
2394            var values = trim(this.properties, compressor, first_in_statement);
2395            return values && AST_Seq.from_array(values);
2396        });
2397        def(AST_ObjectProperty, function(compressor, first_in_statement){
2398            return this.value.drop_side_effect_free(compressor, first_in_statement);
2399        });
2400        def(AST_Array, function(compressor, first_in_statement){
2401            var values = trim(this.elements, compressor, first_in_statement);
2402            return values && AST_Seq.from_array(values);
2403        });
2404        def(AST_Dot, function(compressor, first_in_statement){
2405            if (this.expression.may_throw_on_access(compressor)) return this;
2406            return this.expression.drop_side_effect_free(compressor, first_in_statement);
2407        });
2408        def(AST_Sub, function(compressor, first_in_statement){
2409            if (this.expression.may_throw_on_access(compressor)) return this;
2410            var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
2411            if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
2412            var property = this.property.drop_side_effect_free(compressor);
2413            if (!property) return expression;
2414            return make_node(AST_Seq, this, {
2415                car: expression,
2416                cdr: property
2417            });
2418        });
2419        def(AST_Seq, function(compressor){
2420            var cdr = this.cdr.drop_side_effect_free(compressor);
2421            if (cdr === this.cdr) return this;
2422            if (!cdr) return this.car;
2423            return make_node(AST_Seq, this, {
2424                car: this.car,
2425                cdr: cdr
2426            });
2427        });
2428    })(function(node, func){
2429        node.DEFMETHOD("drop_side_effect_free", func);
2430    });
2431
2432    OPT(AST_SimpleStatement, function(self, compressor){
2433        if (compressor.option("side_effects")) {
2434            var body = self.body;
2435            var node = body.drop_side_effect_free(compressor, true);
2436            if (!node) {
2437                compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
2438                return make_node(AST_EmptyStatement, self);
2439            }
2440            if (node !== body) {
2441                return make_node(AST_SimpleStatement, self, { body: node });
2442            }
2443        }
2444        return self;
2445    });
2446
2447    OPT(AST_DWLoop, function(self, compressor){
2448        if (!compressor.option("loops")) return self;
2449        var cond = self.condition.evaluate(compressor);
2450        if (cond !== self.condition) {
2451            if (cond) {
2452                return make_node(AST_For, self, {
2453                    body: self.body
2454                });
2455            }
2456            if (compressor.option("dead_code") && self instanceof AST_While) {
2457                var a = [];
2458                extract_declarations_from_unreachable_code(compressor, self.body, a);
2459                return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
2460            }
2461            if (self instanceof AST_Do) {
2462                var has_loop_control = false;
2463                var tw = new TreeWalker(function(node) {
2464                    if (node instanceof AST_Scope || has_loop_control) return true;
2465                    if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === self)
2466                        return has_loop_control = true;
2467                });
2468                var parent = compressor.parent();
2469                (parent instanceof AST_LabeledStatement ? parent : self).walk(tw);
2470                if (!has_loop_control) return self.body;
2471            }
2472        }
2473        if (self instanceof AST_While) {
2474            return make_node(AST_For, self, self).optimize(compressor);
2475        }
2476        return self;
2477    });
2478
2479    function if_break_in_loop(self, compressor) {
2480        function drop_it(rest) {
2481            rest = as_statement_array(rest);
2482            if (self.body instanceof AST_BlockStatement) {
2483                self.body = self.body.clone();
2484                self.body.body = rest.concat(self.body.body.slice(1));
2485                self.body = self.body.transform(compressor);
2486            } else {
2487                self.body = make_node(AST_BlockStatement, self.body, {
2488                    body: rest
2489                }).transform(compressor);
2490            }
2491            if_break_in_loop(self, compressor);
2492        }
2493        var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
2494        if (first instanceof AST_If) {
2495            if (first.body instanceof AST_Break
2496                && compressor.loopcontrol_target(first.body) === compressor.self()) {
2497                if (self.condition) {
2498                    self.condition = make_node(AST_Binary, self.condition, {
2499                        left: self.condition,
2500                        operator: "&&",
2501                        right: first.condition.negate(compressor),
2502                    });
2503                } else {
2504                    self.condition = first.condition.negate(compressor);
2505                }
2506                drop_it(first.alternative);
2507            }
2508            else if (first.alternative instanceof AST_Break
2509                     && compressor.loopcontrol_target(first.alternative) === compressor.self()) {
2510                if (self.condition) {
2511                    self.condition = make_node(AST_Binary, self.condition, {
2512                        left: self.condition,
2513                        operator: "&&",
2514                        right: first.condition,
2515                    });
2516                } else {
2517                    self.condition = first.condition;
2518                }
2519                drop_it(first.body);
2520            }
2521        }
2522    };
2523
2524    OPT(AST_For, function(self, compressor){
2525        if (!compressor.option("loops")) return self;
2526        if (self.condition) {
2527            var cond = self.condition.evaluate(compressor);
2528            if (compressor.option("dead_code") && !cond) {
2529                var a = [];
2530                if (self.init instanceof AST_Statement) {
2531                    a.push(self.init);
2532                }
2533                else if (self.init) {
2534                    a.push(make_node(AST_SimpleStatement, self.init, {
2535                        body: self.init
2536                    }));
2537                }
2538                extract_declarations_from_unreachable_code(compressor, self.body, a);
2539                return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
2540            }
2541            if (cond !== self.condition) {
2542                cond = make_node_from_constant(cond, self.condition).transform(compressor);
2543                self.condition = best_of_expression(cond, self.condition);
2544            }
2545        }
2546        if_break_in_loop(self, compressor);
2547        return self;
2548    });
2549
2550    OPT(AST_If, function(self, compressor){
2551        if (is_empty(self.alternative)) self.alternative = null;
2552
2553        if (!compressor.option("conditionals")) return self;
2554        // if condition can be statically determined, warn and drop
2555        // one of the blocks.  note, statically determined implies
2556        // “has no side effects”; also it doesn't work for cases like
2557        // `x && true`, though it probably should.
2558        var cond = self.condition.evaluate(compressor);
2559        if (cond !== self.condition) {
2560            if (cond) {
2561                compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
2562                if (compressor.option("dead_code")) {
2563                    var a = [];
2564                    if (self.alternative) {
2565                        extract_declarations_from_unreachable_code(compressor, self.alternative, a);
2566                    }
2567                    a.push(self.body);
2568                    return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
2569                }
2570            } else {
2571                compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
2572                if (compressor.option("dead_code")) {
2573                    var a = [];
2574                    extract_declarations_from_unreachable_code(compressor, self.body, a);
2575                    if (self.alternative) a.push(self.alternative);
2576                    return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
2577                }
2578            }
2579            cond = make_node_from_constant(cond, self.condition).transform(compressor);
2580            self.condition = best_of_expression(cond, self.condition);
2581        }
2582        var negated = self.condition.negate(compressor);
2583        var self_condition_length = self.condition.print_to_string().length;
2584        var negated_length = negated.print_to_string().length;
2585        var negated_is_best = negated_length < self_condition_length;
2586        if (self.alternative && negated_is_best) {
2587            negated_is_best = false; // because we already do the switch here.
2588            // no need to swap values of self_condition_length and negated_length
2589            // here because they are only used in an equality comparison later on.
2590            self.condition = negated;
2591            var tmp = self.body;
2592            self.body = self.alternative || make_node(AST_EmptyStatement, self);
2593            self.alternative = tmp;
2594        }
2595        if (is_empty(self.body) && is_empty(self.alternative)) {
2596            return make_node(AST_SimpleStatement, self.condition, {
2597                body: self.condition.clone()
2598            }).optimize(compressor);
2599        }
2600        if (self.body instanceof AST_SimpleStatement
2601            && self.alternative instanceof AST_SimpleStatement) {
2602            return make_node(AST_SimpleStatement, self, {
2603                body: make_node(AST_Conditional, self, {
2604                    condition   : self.condition,
2605                    consequent  : self.body.body,
2606                    alternative : self.alternative.body
2607                })
2608            }).optimize(compressor);
2609        }
2610        if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
2611            if (self_condition_length === negated_length && !negated_is_best
2612                && self.condition instanceof AST_Binary && self.condition.operator == "||") {
2613                // although the code length of self.condition and negated are the same,
2614                // negated does not require additional surrounding parentheses.
2615                // see https://github.com/mishoo/UglifyJS2/issues/979
2616                negated_is_best = true;
2617            }
2618            if (negated_is_best) return make_node(AST_SimpleStatement, self, {
2619                body: make_node(AST_Binary, self, {
2620                    operator : "||",
2621                    left     : negated,
2622                    right    : self.body.body
2623                })
2624            }).optimize(compressor);
2625            return make_node(AST_SimpleStatement, self, {
2626                body: make_node(AST_Binary, self, {
2627                    operator : "&&",
2628                    left     : self.condition,
2629                    right    : self.body.body
2630                })
2631            }).optimize(compressor);
2632        }
2633        if (self.body instanceof AST_EmptyStatement
2634            && self.alternative instanceof AST_SimpleStatement) {
2635            return make_node(AST_SimpleStatement, self, {
2636                body: make_node(AST_Binary, self, {
2637                    operator : "||",
2638                    left     : self.condition,
2639                    right    : self.alternative.body
2640                })
2641            }).optimize(compressor);
2642        }
2643        if (self.body instanceof AST_Exit
2644            && self.alternative instanceof AST_Exit
2645            && self.body.TYPE == self.alternative.TYPE) {
2646            return make_node(self.body.CTOR, self, {
2647                value: make_node(AST_Conditional, self, {
2648                    condition   : self.condition,
2649                    consequent  : self.body.value || make_node(AST_Undefined, self.body),
2650                    alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
2651                }).transform(compressor)
2652            }).optimize(compressor);
2653        }
2654        if (self.body instanceof AST_If
2655            && !self.body.alternative
2656            && !self.alternative) {
2657            self = make_node(AST_If, self, {
2658                condition: make_node(AST_Binary, self.condition, {
2659                    operator: "&&",
2660                    left: self.condition,
2661                    right: self.body.condition
2662                }),
2663                body: self.body.body,
2664                alternative: null
2665            });
2666        }
2667        if (aborts(self.body)) {
2668            if (self.alternative) {
2669                var alt = self.alternative;
2670                self.alternative = null;
2671                return make_node(AST_BlockStatement, self, {
2672                    body: [ self, alt ]
2673                }).optimize(compressor);
2674            }
2675        }
2676        if (aborts(self.alternative)) {
2677            var body = self.body;
2678            self.body = self.alternative;
2679            self.condition = negated_is_best ? negated : self.condition.negate(compressor);
2680            self.alternative = null;
2681            return make_node(AST_BlockStatement, self, {
2682                body: [ self, body ]
2683            }).optimize(compressor);
2684        }
2685        return self;
2686    });
2687
2688    OPT(AST_Switch, function(self, compressor){
2689        if (!compressor.option("switches")) return self;
2690        var branch;
2691        var value = self.expression.evaluate(compressor);
2692        if (value !== self.expression) {
2693            var expression = make_node_from_constant(value, self.expression).transform(compressor);
2694            self.expression = best_of_expression(expression, self.expression);
2695        }
2696        if (!compressor.option("dead_code")) return self;
2697        var decl = [];
2698        var body = [];
2699        var default_branch;
2700        var exact_match;
2701        for (var i = 0, len = self.body.length; i < len && !exact_match; i++) {
2702            branch = self.body[i];
2703            if (branch instanceof AST_Default) {
2704                if (!default_branch) {
2705                    default_branch = branch;
2706                } else {
2707                    eliminate_branch(branch, body[body.length - 1]);
2708                }
2709            } else if (value !== self.expression) {
2710                var exp = branch.expression.evaluate(compressor);
2711                if (exp === value) {
2712                    exact_match = branch;
2713                    if (default_branch) {
2714                        var default_index = body.indexOf(default_branch);
2715                        body.splice(default_index, 1);
2716                        eliminate_branch(default_branch, body[default_index - 1]);
2717                        default_branch = null;
2718                    }
2719                } else if (exp !== branch.expression) {
2720                    eliminate_branch(branch, body[body.length - 1]);
2721                    continue;
2722                }
2723            }
2724            if (aborts(branch)) {
2725                var prev = body[body.length - 1];
2726                if (aborts(prev) && prev.body.length == branch.body.length
2727                    && make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) {
2728                    prev.body = [];
2729                }
2730            }
2731            body.push(branch);
2732        }
2733        while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
2734        if (body.length > 0) {
2735            body[0].body = decl.concat(body[0].body);
2736        }
2737        self.body = body;
2738        while (branch = body[body.length - 1]) {
2739            var stat = branch.body[branch.body.length - 1];
2740            if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
2741                branch.body.pop();
2742            if (branch.body.length || branch instanceof AST_Case
2743                && (default_branch || branch.expression.has_side_effects(compressor))) break;
2744            if (body.pop() === default_branch) default_branch = null;
2745        }
2746        if (body.length == 0) {
2747            return make_node(AST_BlockStatement, self, {
2748                body: decl.concat(make_node(AST_SimpleStatement, self.expression, {
2749                    body: self.expression
2750                }))
2751            }).optimize(compressor);
2752        }
2753        if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) {
2754            var has_break = false;
2755            var tw = new TreeWalker(function(node) {
2756                if (has_break
2757                    || node instanceof AST_Lambda
2758                    || node instanceof AST_SimpleStatement) return true;
2759                if (node instanceof AST_Break && tw.loopcontrol_target(node) === self)
2760                    has_break = true;
2761            });
2762            self.walk(tw);
2763            if (!has_break) {
2764                body = body[0].body.slice();
2765                body.unshift(make_node(AST_SimpleStatement, self.expression, {
2766                    body: self.expression
2767                }));
2768                return make_node(AST_BlockStatement, self, {
2769                    body: body
2770                }).optimize(compressor);
2771            }
2772        }
2773        return self;
2774
2775        function eliminate_branch(branch, prev) {
2776            if (prev && !aborts(prev)) {
2777                prev.body = prev.body.concat(branch.body);
2778            } else {
2779                extract_declarations_from_unreachable_code(compressor, branch, decl);
2780            }
2781        }
2782    });
2783
2784    OPT(AST_Try, function(self, compressor){
2785        self.body = tighten_body(self.body, compressor);
2786        if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
2787        if (all(self.body, is_empty)) {
2788            var body = [];
2789            if (self.bcatch) extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
2790            if (self.bfinally) body = body.concat(self.bfinally.body);
2791            return make_node(AST_BlockStatement, self, {
2792                body: body
2793            }).optimize(compressor);
2794        }
2795        return self;
2796    });
2797
2798    AST_Definitions.DEFMETHOD("remove_initializers", function(){
2799        this.definitions.forEach(function(def){ def.value = null });
2800    });
2801
2802    AST_Definitions.DEFMETHOD("to_assignments", function(compressor){
2803        var reduce_vars = compressor.option("reduce_vars");
2804        var assignments = this.definitions.reduce(function(a, def){
2805            if (def.value) {
2806                var name = make_node(AST_SymbolRef, def.name, def.name);
2807                a.push(make_node(AST_Assign, def, {
2808                    operator : "=",
2809                    left     : name,
2810                    right    : def.value
2811                }));
2812                if (reduce_vars) name.definition().fixed = false;
2813            }
2814            return a;
2815        }, []);
2816        if (assignments.length == 0) return null;
2817        return AST_Seq.from_array(assignments);
2818    });
2819
2820    OPT(AST_Definitions, function(self, compressor){
2821        if (self.definitions.length == 0)
2822            return make_node(AST_EmptyStatement, self);
2823        return self;
2824    });
2825
2826    OPT(AST_Call, function(self, compressor){
2827        var exp = self.expression;
2828        if (compressor.option("reduce_vars")
2829            && exp instanceof AST_SymbolRef) {
2830            var def = exp.definition();
2831            var fixed = exp.fixed_value();
2832            if (fixed instanceof AST_Defun) {
2833                def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
2834            }
2835            if (fixed instanceof AST_Function) {
2836                exp = fixed;
2837                if (compressor.option("unused")
2838                    && def.references.length == 1
2839                    && !(def.scope.uses_arguments
2840                        && def.orig[0] instanceof AST_SymbolFunarg)
2841                    && !def.scope.uses_eval
2842                    && compressor.find_parent(AST_Scope) === def.scope) {
2843                    self.expression = exp;
2844                }
2845            }
2846        }
2847        if (compressor.option("unused")
2848            && exp instanceof AST_Function
2849            && !exp.uses_arguments
2850            && !exp.uses_eval) {
2851            var pos = 0, last = 0;
2852            for (var i = 0, len = self.args.length; i < len; i++) {
2853                var trim = i >= exp.argnames.length;
2854                if (trim || exp.argnames[i].__unused) {
2855                    var node = self.args[i].drop_side_effect_free(compressor);
2856                    if (node) {
2857                        self.args[pos++] = node;
2858                    } else if (!trim) {
2859                        self.args[pos++] = make_node(AST_Number, self.args[i], {
2860                            value: 0
2861                        });
2862                        continue;
2863                    }
2864                } else {
2865                    self.args[pos++] = self.args[i];
2866                }
2867                last = pos;
2868            }
2869            self.args.length = last;
2870        }
2871        if (compressor.option("unsafe")) {
2872            if (exp instanceof AST_SymbolRef && exp.undeclared()) {
2873                switch (exp.name) {
2874                  case "Array":
2875                    if (self.args.length != 1) {
2876                        return make_node(AST_Array, self, {
2877                            elements: self.args
2878                        }).optimize(compressor);
2879                    }
2880                    break;
2881                  case "Object":
2882                    if (self.args.length == 0) {
2883                        return make_node(AST_Object, self, {
2884                            properties: []
2885                        });
2886                    }
2887                    break;
2888                  case "String":
2889                    if (self.args.length == 0) return make_node(AST_String, self, {
2890                        value: ""
2891                    });
2892                    if (self.args.length <= 1) return make_node(AST_Binary, self, {
2893                        left: self.args[0],
2894                        operator: "+",
2895                        right: make_node(AST_String, self, { value: "" })
2896                    }).optimize(compressor);
2897                    break;
2898                  case "Number":
2899                    if (self.args.length == 0) return make_node(AST_Number, self, {
2900                        value: 0
2901                    });
2902                    if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
2903                        expression: self.args[0],
2904                        operator: "+"
2905                    }).optimize(compressor);
2906                  case "Boolean":
2907                    if (self.args.length == 0) return make_node(AST_False, self);
2908                    if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
2909                        expression: make_node(AST_UnaryPrefix, self, {
2910                            expression: self.args[0],
2911                            operator: "!"
2912                        }),
2913                        operator: "!"
2914                    }).optimize(compressor);
2915                    break;
2916                  case "Function":
2917                    // new Function() => function(){}
2918                    if (self.args.length == 0) return make_node(AST_Function, self, {
2919                        argnames: [],
2920                        body: []
2921                    });
2922                    if (all(self.args, function(x){ return x instanceof AST_String })) {
2923                        // quite a corner-case, but we can handle it:
2924                        //   https://github.com/mishoo/UglifyJS2/issues/203
2925                        // if the code argument is a constant, then we can minify it.
2926                        try {
2927                            var code = "(function(" + self.args.slice(0, -1).map(function(arg){
2928                                return arg.value;
2929                            }).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
2930                            var ast = parse(code);
2931                            ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
2932                            var comp = new Compressor(compressor.options);
2933                            ast = ast.transform(comp);
2934                            ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
2935                            ast.mangle_names();
2936                            var fun;
2937                            try {
2938                                ast.walk(new TreeWalker(function(node){
2939                                    if (node instanceof AST_Lambda) {
2940                                        fun = node;
2941                                        throw ast;
2942                                    }
2943                                }));
2944                            } catch(ex) {
2945                                if (ex !== ast) throw ex;
2946                            };
2947                            if (!fun) return self;
2948                            var args = fun.argnames.map(function(arg, i){
2949                                return make_node(AST_String, self.args[i], {
2950                                    value: arg.print_to_string()
2951                                });
2952                            });
2953                            var code = OutputStream();
2954                            AST_BlockStatement.prototype._codegen.call(fun, fun, code);
2955                            code = code.toString().replace(/^\{|\}$/g, "");
2956                            args.push(make_node(AST_String, self.args[self.args.length - 1], {
2957                                value: code
2958                            }));
2959                            self.args = args;
2960                            return self;
2961                        } catch(ex) {
2962                            if (ex instanceof JS_Parse_Error) {
2963                                compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
2964                                compressor.warn(ex.toString());
2965                            } else {
2966                                console.log(ex);
2967                                throw ex;
2968                            }
2969                        }
2970                    }
2971                    break;
2972                }
2973            }
2974            else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
2975                return make_node(AST_Binary, self, {
2976                    left: make_node(AST_String, self, { value: "" }),
2977                    operator: "+",
2978                    right: exp.expression
2979                }).optimize(compressor);
2980            }
2981            else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
2982                var separator;
2983                if (self.args.length > 0) {
2984                    separator = self.args[0].evaluate(compressor);
2985                    if (separator === self.args[0]) break EXIT; // not a constant
2986                }
2987                var elements = [];
2988                var consts = [];
2989                exp.expression.elements.forEach(function(el) {
2990                    var value = el.evaluate(compressor);
2991                    if (value !== el) {
2992                        consts.push(value);
2993                    } else {
2994                        if (consts.length > 0) {
2995                            elements.push(make_node(AST_String, self, {
2996                                value: consts.join(separator)
2997                            }));
2998                            consts.length = 0;
2999                        }
3000                        elements.push(el);
3001                    }
3002                });
3003                if (consts.length > 0) {
3004                    elements.push(make_node(AST_String, self, {
3005                        value: consts.join(separator)
3006                    }));
3007                }
3008                if (elements.length == 0) return make_node(AST_String, self, { value: "" });
3009                if (elements.length == 1) {
3010                    if (elements[0].is_string(compressor)) {
3011                        return elements[0];
3012                    }
3013                    return make_node(AST_Binary, elements[0], {
3014                        operator : "+",
3015                        left     : make_node(AST_String, self, { value: "" }),
3016                        right    : elements[0]
3017                    });
3018                }
3019                if (separator == "") {
3020                    var first;
3021                    if (elements[0].is_string(compressor)
3022                        || elements[1].is_string(compressor)) {
3023                        first = elements.shift();
3024                    } else {
3025                        first = make_node(AST_String, self, { value: "" });
3026                    }
3027                    return elements.reduce(function(prev, el){
3028                        return make_node(AST_Binary, el, {
3029                            operator : "+",
3030                            left     : prev,
3031                            right    : el
3032                        });
3033                    }, first).optimize(compressor);
3034                }
3035                // need this awkward cloning to not affect original element
3036                // best_of will decide which one to get through.
3037                var node = self.clone();
3038                node.expression = node.expression.clone();
3039                node.expression.expression = node.expression.expression.clone();
3040                node.expression.expression.elements = elements;
3041                return best_of(compressor, self, node);
3042            }
3043            else if (exp instanceof AST_Dot && exp.expression.is_string(compressor) && exp.property == "charAt") {
3044                var arg = self.args[0];
3045                var index = arg ? arg.evaluate(compressor) : 0;
3046                if (index !== arg) {
3047                    return make_node(AST_Sub, exp, {
3048                        expression: exp.expression,
3049                        property: make_node_from_constant(index | 0, arg || exp)
3050                    }).optimize(compressor);
3051                }
3052            }
3053        }
3054        if (exp instanceof AST_Function) {
3055            if (exp.body[0] instanceof AST_Return) {
3056                var value = exp.body[0].value;
3057                if (!value || value.is_constant()) {
3058                    var args = self.args.concat(value || make_node(AST_Undefined, self));
3059                    return AST_Seq.from_array(args).transform(compressor);
3060                }
3061            }
3062            if (compressor.option("side_effects") && all(exp.body, is_empty)) {
3063                var args = self.args.concat(make_node(AST_Undefined, self));
3064                return AST_Seq.from_array(args).transform(compressor);
3065            }
3066        }
3067        if (compressor.option("drop_console")) {
3068            if (exp instanceof AST_PropAccess) {
3069                var name = exp.expression;
3070                while (name.expression) {
3071                    name = name.expression;
3072                }
3073                if (name instanceof AST_SymbolRef
3074                    && name.name == "console"
3075                    && name.undeclared()) {
3076                    return make_node(AST_Undefined, self).optimize(compressor);
3077                }
3078            }
3079        }
3080        if (compressor.option("negate_iife")
3081            && compressor.parent() instanceof AST_SimpleStatement
3082            && is_iife_call(self)) {
3083            return self.negate(compressor, true);
3084        }
3085        return self;
3086    });
3087
3088    OPT(AST_New, function(self, compressor){
3089        if (compressor.option("unsafe")) {
3090            var exp = self.expression;
3091            if (exp instanceof AST_SymbolRef && exp.undeclared()) {
3092                switch (exp.name) {
3093                  case "Object":
3094                  case "RegExp":
3095                  case "Function":
3096                  case "Error":
3097                  case "Array":
3098                    return make_node(AST_Call, self, self).transform(compressor);
3099                }
3100            }
3101        }
3102        return self;
3103    });
3104
3105    OPT(AST_Seq, function(self, compressor){
3106        if (!compressor.option("side_effects"))
3107            return self;
3108        self.car = self.car.drop_side_effect_free(compressor, first_in_statement(compressor));
3109        if (!self.car) return maintain_this_binding(compressor.parent(), self, self.cdr);
3110        if (compressor.option("cascade")) {
3111            var left;
3112            if (self.car instanceof AST_Assign
3113                && !self.car.left.has_side_effects(compressor)) {
3114                left = self.car.left;
3115            } else if (self.car instanceof AST_Unary
3116                && (self.car.operator == "++" || self.car.operator == "--")) {
3117                left = self.car.expression;
3118            }
3119            if (left
3120                && !(left instanceof AST_SymbolRef
3121                    && (left.definition().orig[0] instanceof AST_SymbolLambda
3122                        || is_reference_const(left)))) {
3123                var parent, field;
3124                var cdr = self.cdr;
3125                while (true) {
3126                    if (cdr.equivalent_to(left)) {
3127                        var car = self.car instanceof AST_UnaryPostfix ? make_node(AST_UnaryPrefix, self.car, {
3128                            operator: self.car.operator,
3129                            expression: left
3130                        }) : self.car;
3131                        if (parent) {
3132                            parent[field] = car;
3133                            return self.cdr;
3134                        }
3135                        return car;
3136                    }
3137                    if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
3138                        if (cdr.left.is_constant()) {
3139                            if (cdr.operator == "||" || cdr.operator == "&&") break;
3140                            field = "right";
3141                        } else {
3142                            field = "left";
3143                        }
3144                    } else if (cdr instanceof AST_Call
3145                        || cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
3146                        field = "expression";
3147                    } else break;
3148                    parent = cdr;
3149                    cdr = cdr[field];
3150                }
3151            }
3152        }
3153        if (is_undefined(self.cdr, compressor)) {
3154            return make_node(AST_UnaryPrefix, self, {
3155                operator   : "void",
3156                expression : self.car
3157            });
3158        }
3159        return self;
3160    });
3161
3162    AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
3163        if (compressor.option("sequences")) {
3164            if (this.expression instanceof AST_Seq) {
3165                var seq = this.expression;
3166                var x = seq.to_array();
3167                var e = this.clone();
3168                e.expression = x.pop();
3169                x.push(e);
3170                seq = AST_Seq.from_array(x).transform(compressor);
3171                return seq;
3172            }
3173        }
3174        return this;
3175    });
3176
3177    OPT(AST_UnaryPostfix, function(self, compressor){
3178        return self.lift_sequences(compressor);
3179    });
3180
3181    OPT(AST_UnaryPrefix, function(self, compressor){
3182        var e = self.expression;
3183        if (self.operator == "delete"
3184            && !(e instanceof AST_SymbolRef
3185                || e instanceof AST_PropAccess
3186                || e instanceof AST_NaN
3187                || e instanceof AST_Infinity
3188                || e instanceof AST_Undefined)) {
3189            if (e instanceof AST_Seq) {
3190                e = e.to_array();
3191                e.push(make_node(AST_True, self));
3192                return AST_Seq.from_array(e).optimize(compressor);
3193            }
3194            return make_node(AST_Seq, self, {
3195                car: e,
3196                cdr: make_node(AST_True, self)
3197            }).optimize(compressor);
3198        }
3199        var seq = self.lift_sequences(compressor);
3200        if (seq !== self) {
3201            return seq;
3202        }
3203        if (compressor.option("side_effects") && self.operator == "void") {
3204            e = e.drop_side_effect_free(compressor);
3205            if (e) {
3206                self.expression = e;
3207                return self;
3208            } else {
3209                return make_node(AST_Undefined, self).optimize(compressor);
3210            }
3211        }
3212        if (compressor.option("booleans") && compressor.in_boolean_context()) {
3213            switch (self.operator) {
3214              case "!":
3215                if (e instanceof AST_UnaryPrefix && e.operator == "!") {
3216                    // !!foo ==> foo, if we're in boolean context
3217                    return e.expression;
3218                }
3219                if (e instanceof AST_Binary) {
3220                    self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
3221                }
3222                break;
3223              case "typeof":
3224                // typeof always returns a non-empty string, thus it's
3225                // always true in booleans
3226                compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
3227                return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_node(AST_Seq, self, {
3228                    car: e,
3229                    cdr: make_node(AST_True, self)
3230                })).optimize(compressor);
3231            }
3232        }
3233        if (self.operator == "-" && e instanceof AST_Infinity) {
3234            e = e.transform(compressor);
3235        }
3236        if (e instanceof AST_Binary
3237            && (self.operator == "+" || self.operator == "-")
3238            && (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
3239            return make_node(AST_Binary, self, {
3240                operator: e.operator,
3241                left: make_node(AST_UnaryPrefix, e.left, {
3242                    operator: self.operator,
3243                    expression: e.left
3244                }),
3245                right: e.right
3246            });
3247        }
3248        // avoids infinite recursion of numerals
3249        if (self.operator != "-"
3250            || !(e instanceof AST_Number || e instanceof AST_Infinity)) {
3251            var ev = self.evaluate(compressor);
3252            if (ev !== self) {
3253                ev = make_node_from_constant(ev, self).optimize(compressor);
3254                return best_of(compressor, ev, self);
3255            }
3256        }
3257        return self;
3258    });
3259
3260    AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
3261        if (compressor.option("sequences")) {
3262            if (this.left instanceof AST_Seq) {
3263                var seq = this.left;
3264                var x = seq.to_array();
3265                var e = this.clone();
3266                e.left = x.pop();
3267                x.push(e);
3268                return AST_Seq.from_array(x).optimize(compressor);
3269            }
3270            if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
3271                var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
3272                var root = this.right.clone();
3273                var cursor, seq = root;
3274                while (assign || !seq.car.has_side_effects(compressor)) {
3275                    cursor = seq;
3276                    if (seq.cdr instanceof AST_Seq) {
3277                        seq = seq.cdr = seq.cdr.clone();
3278                    } else break;
3279                }
3280                if (cursor) {
3281                    var e = this.clone();
3282                    e.right = cursor.cdr;
3283                    cursor.cdr = e;
3284                    return root.optimize(compressor);
3285                }
3286            }
3287        }
3288        return this;
3289    });
3290
3291    var commutativeOperators = makePredicate("== === != !== * & | ^");
3292
3293    OPT(AST_Binary, function(self, compressor){
3294        function reversible() {
3295            return self.left.is_constant()
3296                || self.right.is_constant()
3297                || !self.left.has_side_effects(compressor)
3298                    && !self.right.has_side_effects(compressor);
3299        }
3300        function reverse(op) {
3301            if (reversible()) {
3302                if (op) self.operator = op;
3303                var tmp = self.left;
3304                self.left = self.right;
3305                self.right = tmp;
3306            }
3307        }
3308        if (commutativeOperators(self.operator)) {
3309            if (self.right.is_constant()
3310                && !self.left.is_constant()) {
3311                // if right is a constant, whatever side effects the
3312                // left side might have could not influence the
3313                // result.  hence, force switch.
3314
3315                if (!(self.left instanceof AST_Binary
3316                      && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
3317                    reverse();
3318                }
3319            }
3320        }
3321        self = self.lift_sequences(compressor);
3322        if (compressor.option("comparisons")) switch (self.operator) {
3323          case "===":
3324          case "!==":
3325            if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
3326                (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
3327                (self.left.is_boolean() && self.right.is_boolean())) {
3328                self.operator = self.operator.substr(0, 2);
3329            }
3330            // XXX: intentionally falling down to the next case
3331          case "==":
3332          case "!=":
3333            // "undefined" == typeof x => undefined === x
3334            if (self.left instanceof AST_String
3335                && self.left.value == "undefined"
3336                && self.right instanceof AST_UnaryPrefix
3337                && self.right.operator == "typeof") {
3338                var expr = self.right.expression;
3339                if (expr instanceof AST_SymbolRef ? !expr.undeclared()
3340                    : !(expr instanceof AST_PropAccess) || compressor.option("screw_ie8")) {
3341                    self.right = expr;
3342                    self.left = make_node(AST_Undefined, self.left).optimize(compressor);
3343                    if (self.operator.length == 2) self.operator += "=";
3344                }
3345            }
3346            break;
3347        }
3348        if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
3349            var ll = self.left.evaluate(compressor);
3350            var rr = self.right.evaluate(compressor);
3351            if (ll && typeof ll == "string") {
3352                compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
3353                return make_node(AST_Seq, self, {
3354                    car: self.right,
3355                    cdr: make_node(AST_True, self)
3356                }).optimize(compressor);
3357            }
3358            if (rr && typeof rr == "string") {
3359                compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
3360                return make_node(AST_Seq, self, {
3361                    car: self.left,
3362                    cdr: make_node(AST_True, self)
3363                }).optimize(compressor);
3364            }
3365        }
3366        if (compressor.option("comparisons") && self.is_boolean()) {
3367            if (!(compressor.parent() instanceof AST_Binary)
3368                || compressor.parent() instanceof AST_Assign) {
3369                var negated = make_node(AST_UnaryPrefix, self, {
3370                    operator: "!",
3371                    expression: self.negate(compressor, first_in_statement(compressor))
3372                });
3373                self = best_of(compressor, self, negated);
3374            }
3375            if (compressor.option("unsafe_comps")) {
3376                switch (self.operator) {
3377                  case "<": reverse(">"); break;
3378                  case "<=": reverse(">="); break;
3379                }
3380            }
3381        }
3382        if (self.operator == "+") {
3383            if (self.right instanceof AST_String
3384                && self.right.getValue() == ""
3385                && self.left.is_string(compressor)) {
3386                return self.left;
3387            }
3388            if (self.left instanceof AST_String
3389                && self.left.getValue() == ""
3390                && self.right.is_string(compressor)) {
3391                return self.right;
3392            }
3393            if (self.left instanceof AST_Binary
3394                && self.left.operator == "+"
3395                && self.left.left instanceof AST_String
3396                && self.left.left.getValue() == ""
3397                && self.right.is_string(compressor)) {
3398                self.left = self.left.right;
3399                return self.transform(compressor);
3400            }
3401        }
3402        if (compressor.option("evaluate")) {
3403            switch (self.operator) {
3404              case "&&":
3405                var ll = self.left.evaluate(compressor);
3406                if (!ll) {
3407                    compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
3408                    return maintain_this_binding(compressor.parent(), self, self.left).optimize(compressor);
3409                } else if (ll !== self.left) {
3410                    compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
3411                    return maintain_this_binding(compressor.parent(), self, self.right).optimize(compressor);
3412                }
3413                if (compressor.option("booleans") && compressor.in_boolean_context()) {
3414                    var rr = self.right.evaluate(compressor);
3415                    if (!rr) {
3416                        compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
3417                        return make_node(AST_Seq, self, {
3418                            car: self.left,
3419                            cdr: make_node(AST_False, self)
3420                        }).optimize(compressor);
3421                    } else if (rr !== self.right) {
3422                        compressor.warn("Dropping side-effect-free && in boolean context [{file}:{line},{col}]", self.start);
3423                        return self.left.optimize(compressor);
3424                    }
3425                }
3426                break;
3427              case "||":
3428                var ll = self.left.evaluate(compressor);
3429                if (!ll) {
3430                    compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
3431                    return maintain_this_binding(compressor.parent(), self, self.right).optimize(compressor);
3432                } else if (ll !== self.left) {
3433                    compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
3434                    return maintain_this_binding(compressor.parent(), self, self.left).optimize(compressor);
3435                }
3436                if (compressor.option("booleans") && compressor.in_boolean_context()) {
3437                    var rr = self.right.evaluate(compressor);
3438                    if (!rr) {
3439                        compressor.warn("Dropping side-effect-free || in boolean context [{file}:{line},{col}]", self.start);
3440                        return self.left.optimize(compressor);
3441                    } else if (rr !== self.right) {
3442                        compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
3443                        return make_node(AST_Seq, self, {
3444                            car: self.left,
3445                            cdr: make_node(AST_True, self)
3446                        }).optimize(compressor);
3447                    }
3448                }
3449                break;
3450            }
3451            var associative = true;
3452            switch (self.operator) {
3453              case "+":
3454                // "foo" + ("bar" + x) => "foobar" + x
3455                if (self.left instanceof AST_Constant
3456                    && self.right instanceof AST_Binary
3457                    && self.right.operator == "+"
3458                    && self.right.left instanceof AST_Constant
3459                    && self.right.is_string(compressor)) {
3460                    self = make_node(AST_Binary, self, {
3461                        operator: "+",
3462                        left: make_node(AST_String, self.left, {
3463                            value: "" + self.left.getValue() + self.right.left.getValue(),
3464                            start: self.left.start,
3465                            end: self.right.left.end
3466                        }),
3467                        right: self.right.right
3468                    });
3469                }
3470                // (x + "foo") + "bar" => x + "foobar"
3471                if (self.right instanceof AST_Constant
3472                    && self.left instanceof AST_Binary
3473                    && self.left.operator == "+"
3474                    && self.left.right instanceof AST_Constant
3475                    && self.left.is_string(compressor)) {
3476                    self = make_node(AST_Binary, self, {
3477                        operator: "+",
3478                        left: self.left.left,
3479                        right: make_node(AST_String, self.right, {
3480                            value: "" + self.left.right.getValue() + self.right.getValue(),
3481                            start: self.left.right.start,
3482                            end: self.right.end
3483                        })
3484                    });
3485                }
3486                // (x + "foo") + ("bar" + y) => (x + "foobar") + y
3487                if (self.left instanceof AST_Binary
3488                    && self.left.operator == "+"
3489                    && self.left.is_string(compressor)
3490                    && self.left.right instanceof AST_Constant
3491                    && self.right instanceof AST_Binary
3492                    && self.right.operator == "+"
3493                    && self.right.left instanceof AST_Constant
3494                    && self.right.is_string(compressor)) {
3495                    self = make_node(AST_Binary, self, {
3496                        operator: "+",
3497                        left: make_node(AST_Binary, self.left, {
3498                            operator: "+",
3499                            left: self.left.left,
3500                            right: make_node(AST_String, self.left.right, {
3501                                value: "" + self.left.right.getValue() + self.right.left.getValue(),
3502                                start: self.left.right.start,
3503                                end: self.right.left.end
3504                            })
3505                        }),
3506                        right: self.right.right
3507                    });
3508                }
3509                // a + -b => a - b
3510                if (self.right instanceof AST_UnaryPrefix
3511                    && self.right.operator == "-"
3512                    && self.left.is_number(compressor)) {
3513                    self = make_node(AST_Binary, self, {
3514                        operator: "-",
3515                        left: self.left,
3516                        right: self.right.expression
3517                    });
3518                    break;
3519                }
3520                // -a + b => b - a
3521                if (self.left instanceof AST_UnaryPrefix
3522                    && self.left.operator == "-"
3523                    && reversible()
3524                    && self.right.is_number(compressor)) {
3525                    self = make_node(AST_Binary, self, {
3526                        operator: "-",
3527                        left: self.right,
3528                        right: self.left.expression
3529                    });
3530                    break;
3531                }
3532              case "*":
3533                associative = compressor.option("unsafe_math");
3534              case "&":
3535              case "|":
3536              case "^":
3537                // a + +b => +b + a
3538                if (self.left.is_number(compressor)
3539                    && self.right.is_number(compressor)
3540                    && reversible()
3541                    && !(self.left instanceof AST_Binary
3542                        && self.left.operator != self.operator
3543                        && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
3544                    var reversed = make_node(AST_Binary, self, {
3545                        operator: self.operator,
3546                        left: self.right,
3547                        right: self.left
3548                    });
3549                    if (self.right instanceof AST_Constant
3550                        && !(self.left instanceof AST_Constant)) {
3551                        self = best_of(compressor, reversed, self);
3552                    } else {
3553                        self = best_of(compressor, self, reversed);
3554                    }
3555                }
3556                if (associative && self.is_number(compressor)) {
3557                    // a + (b + c) => (a + b) + c
3558                    if (self.right instanceof AST_Binary
3559                        && self.right.operator == self.operator) {
3560                        self = make_node(AST_Binary, self, {
3561                            operator: self.operator,
3562                            left: make_node(AST_Binary, self.left, {
3563                                operator: self.operator,
3564                                left: self.left,
3565                                right: self.right.left,
3566                                start: self.left.start,
3567                                end: self.right.left.end
3568                            }),
3569                            right: self.right.right
3570                        });
3571                    }
3572                    // (n + 2) + 3 => 5 + n
3573                    // (2 * n) * 3 => 6 + n
3574                    if (self.right instanceof AST_Constant
3575                        && self.left instanceof AST_Binary
3576                        && self.left.operator == self.operator) {
3577                        if (self.left.left instanceof AST_Constant) {
3578                            self = make_node(AST_Binary, self, {
3579                                operator: self.operator,
3580                                left: make_node(AST_Binary, self.left, {
3581                                    operator: self.operator,
3582                                    left: self.left.left,
3583                                    right: self.right,
3584                                    start: self.left.left.start,
3585                                    end: self.right.end
3586                                }),
3587                                right: self.left.right
3588                            });
3589                        } else if (self.left.right instanceof AST_Constant) {
3590                            self = make_node(AST_Binary, self, {
3591                                operator: self.operator,
3592                                left: make_node(AST_Binary, self.left, {
3593                                    operator: self.operator,
3594                                    left: self.left.right,
3595                                    right: self.right,
3596                                    start: self.left.right.start,
3597                                    end: self.right.end
3598                                }),
3599                                right: self.left.left
3600                            });
3601                        }
3602                    }
3603                    // (a | 1) | (2 | d) => (3 | a) | b
3604                    if (self.left instanceof AST_Binary
3605                        && self.left.operator == self.operator
3606                        && self.left.right instanceof AST_Constant
3607                        && self.right instanceof AST_Binary
3608                        && self.right.operator == self.operator
3609                        && self.right.left instanceof AST_Constant) {
3610                        self = make_node(AST_Binary, self, {
3611                            operator: self.operator,
3612                            left: make_node(AST_Binary, self.left, {
3613                                operator: self.operator,
3614                                left: make_node(AST_Binary, self.left.left, {
3615                                    operator: self.operator,
3616                                    left: self.left.right,
3617                                    right: self.right.left,
3618                                    start: self.left.right.start,
3619                                    end: self.right.left.end
3620                                }),
3621                                right: self.left.left
3622                            }),
3623                            right: self.right.right
3624                        });
3625                    }
3626                }
3627            }
3628        }
3629        // x && (y && z)  ==>  x && y && z
3630        // x || (y || z)  ==>  x || y || z
3631        // x + ("y" + z)  ==>  x + "y" + z
3632        // "x" + (y + "z")==>  "x" + y + "z"
3633        if (self.right instanceof AST_Binary
3634            && self.right.operator == self.operator
3635            && (self.operator == "&&"
3636                || self.operator == "||"
3637                || (self.operator == "+"
3638                    && (self.right.left.is_string(compressor)
3639                        || (self.left.is_string(compressor)
3640                            && self.right.right.is_string(compressor))))))
3641        {
3642            self.left = make_node(AST_Binary, self.left, {
3643                operator : self.operator,
3644                left     : self.left,
3645                right    : self.right.left
3646            });
3647            self.right = self.right.right;
3648            return self.transform(compressor);
3649        }
3650        var ev = self.evaluate(compressor);
3651        if (ev !== self) {
3652            ev = make_node_from_constant(ev, self).optimize(compressor);
3653            return best_of(compressor, ev, self);
3654        }
3655        return self;
3656    });
3657
3658    OPT(AST_SymbolRef, function(self, compressor){
3659        var def = self.resolve_defines(compressor);
3660        if (def) {
3661            return def.optimize(compressor);
3662        }
3663        // testing against !self.scope.uses_with first is an optimization
3664        if (compressor.option("screw_ie8")
3665            && self.undeclared()
3666            && (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
3667            switch (self.name) {
3668              case "undefined":
3669                return make_node(AST_Undefined, self).optimize(compressor);
3670              case "NaN":
3671                return make_node(AST_NaN, self).optimize(compressor);
3672              case "Infinity":
3673                return make_node(AST_Infinity, self).optimize(compressor);
3674            }
3675        }
3676        if (compressor.option("evaluate")
3677            && compressor.option("reduce_vars")
3678            && is_lhs(self, compressor.parent()) !== self) {
3679            var d = self.definition();
3680            var fixed = self.fixed_value();
3681            if (fixed) {
3682                if (d.should_replace === undefined) {
3683                    var init = fixed.evaluate(compressor);
3684                    if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
3685                        init = make_node_from_constant(init, fixed);
3686                        var value = init.optimize(compressor).print_to_string().length;
3687                        var fn;
3688                        if (has_symbol_ref(fixed)) {
3689                            fn = function() {
3690                                var result = init.optimize(compressor);
3691                                return result === init ? result.clone(true) : result;
3692                            };
3693                        } else {
3694                            value = Math.min(value, fixed.print_to_string().length);
3695                            fn = function() {
3696                                var result = best_of_expression(init.optimize(compressor), fixed);
3697                                return result === init || result === fixed ? result.clone(true) : result;
3698                            };
3699                        }
3700                        var name = d.name.length;
3701                        var overhead = 0;
3702                        if (compressor.option("unused") && (!d.global || compressor.option("toplevel"))) {
3703                            overhead = (name + 2 + value) / d.references.length;
3704                        }
3705                        d.should_replace = value <= name + overhead ? fn : false;
3706                    } else {
3707                        d.should_replace = false;
3708                    }
3709                }
3710                if (d.should_replace) {
3711                    return d.should_replace();
3712                }
3713            }
3714        }
3715        return self;
3716
3717        function has_symbol_ref(value) {
3718            var found;
3719            value.walk(new TreeWalker(function(node) {
3720                if (node instanceof AST_SymbolRef) found = true;
3721                if (found) return true;
3722            }));
3723            return found;
3724        }
3725    });
3726
3727    function is_atomic(lhs, self) {
3728        return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
3729    }
3730
3731    OPT(AST_Undefined, function(self, compressor){
3732        if (compressor.option("unsafe")) {
3733            var undef = find_variable(compressor, "undefined");
3734            if (undef) {
3735                var ref = make_node(AST_SymbolRef, self, {
3736                    name   : "undefined",
3737                    scope  : undef.scope,
3738                    thedef : undef
3739                });
3740                ref.is_undefined = true;
3741                return ref;
3742            }
3743        }
3744        var lhs = is_lhs(compressor.self(), compressor.parent());
3745        if (lhs && is_atomic(lhs, self)) return self;
3746        return make_node(AST_UnaryPrefix, self, {
3747            operator: "void",
3748            expression: make_node(AST_Number, self, {
3749                value: 0
3750            })
3751        });
3752    });
3753
3754    OPT(AST_Infinity, function(self, compressor){
3755        var lhs = is_lhs(compressor.self(), compressor.parent());
3756        if (lhs && is_atomic(lhs, self)) return self;
3757        if (compressor.option("keep_infinity")
3758            && !(lhs && !is_atomic(lhs, self))
3759            && !find_variable(compressor, "Infinity"))
3760            return self;
3761        return make_node(AST_Binary, self, {
3762            operator: "/",
3763            left: make_node(AST_Number, self, {
3764                value: 1
3765            }),
3766            right: make_node(AST_Number, self, {
3767                value: 0
3768            })
3769        });
3770    });
3771
3772    OPT(AST_NaN, function(self, compressor){
3773        var lhs = is_lhs(compressor.self(), compressor.parent());
3774        if (lhs && !is_atomic(lhs, self)
3775            || find_variable(compressor, "NaN")) {
3776            return make_node(AST_Binary, self, {
3777                operator: "/",
3778                left: make_node(AST_Number, self, {
3779                    value: 0
3780                }),
3781                right: make_node(AST_Number, self, {
3782                    value: 0
3783                })
3784            });
3785        }
3786        return self;
3787    });
3788
3789    var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
3790    var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ];
3791    OPT(AST_Assign, function(self, compressor){
3792        self = self.lift_sequences(compressor);
3793        if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
3794            // x = expr1 OP expr2
3795            if (self.right.left instanceof AST_SymbolRef
3796                && self.right.left.name == self.left.name
3797                && member(self.right.operator, ASSIGN_OPS)) {
3798                // x = x - 2  --->  x -= 2
3799                self.operator = self.right.operator + "=";
3800                self.right = self.right.right;
3801            }
3802            else if (self.right.right instanceof AST_SymbolRef
3803                && self.right.right.name == self.left.name
3804                && member(self.right.operator, ASSIGN_OPS_COMMUTATIVE)
3805                && !self.right.left.has_side_effects(compressor)) {
3806                // x = 2 & x  --->  x &= 2
3807                self.operator = self.right.operator + "=";
3808                self.right = self.right.left;
3809            }
3810        }
3811        return self;
3812    });
3813
3814    OPT(AST_Conditional, function(self, compressor){
3815        if (!compressor.option("conditionals")) return self;
3816        if (self.condition instanceof AST_Seq) {
3817            var car = self.condition.car;
3818            self.condition = self.condition.cdr;
3819            return AST_Seq.cons(car, self);
3820        }
3821        var cond = self.condition.evaluate(compressor);
3822        if (cond !== self.condition) {
3823            if (cond) {
3824                compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
3825                return maintain_this_binding(compressor.parent(), self, self.consequent);
3826            } else {
3827                compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
3828                return maintain_this_binding(compressor.parent(), self, self.alternative);
3829            }
3830        }
3831        var negated = cond.negate(compressor, first_in_statement(compressor));
3832        if (best_of(compressor, cond, negated) === negated) {
3833            self = make_node(AST_Conditional, self, {
3834                condition: negated,
3835                consequent: self.alternative,
3836                alternative: self.consequent
3837            });
3838        }
3839        var condition = self.condition;
3840        var consequent = self.consequent;
3841        var alternative = self.alternative;
3842        // x?x:y --> x||y
3843        if (condition instanceof AST_SymbolRef
3844            && consequent instanceof AST_SymbolRef
3845            && condition.definition() === consequent.definition()) {
3846            return make_node(AST_Binary, self, {
3847                operator: "||",
3848                left: condition,
3849                right: alternative
3850            });
3851        }
3852        // if (foo) exp = something; else exp = something_else;
3853        //                   |
3854        //                   v
3855        // exp = foo ? something : something_else;
3856        if (consequent instanceof AST_Assign
3857            && alternative instanceof AST_Assign
3858            && consequent.operator == alternative.operator
3859            && consequent.left.equivalent_to(alternative.left)
3860            && (!self.condition.has_side_effects(compressor)
3861                || consequent.operator == "="
3862                    && !consequent.left.has_side_effects(compressor))) {
3863            return make_node(AST_Assign, self, {
3864                operator: consequent.operator,
3865                left: consequent.left,
3866                right: make_node(AST_Conditional, self, {
3867                    condition: self.condition,
3868                    consequent: consequent.right,
3869                    alternative: alternative.right
3870                })
3871            });
3872        }
3873        // x ? y(a) : y(b) --> y(x ? a : b)
3874        if (consequent instanceof AST_Call
3875            && alternative.TYPE === consequent.TYPE
3876            && consequent.args.length == 1
3877            && alternative.args.length == 1
3878            && consequent.expression.equivalent_to(alternative.expression)
3879            && !consequent.expression.has_side_effects(compressor)) {
3880            consequent.args[0] = make_node(AST_Conditional, self, {
3881                condition: self.condition,
3882                consequent: consequent.args[0],
3883                alternative: alternative.args[0]
3884            });
3885            return consequent;
3886        }
3887        // x?y?z:a:a --> x&&y?z:a
3888        if (consequent instanceof AST_Conditional
3889            && consequent.alternative.equivalent_to(alternative)) {
3890            return make_node(AST_Conditional, self, {
3891                condition: make_node(AST_Binary, self, {
3892                    left: self.condition,
3893                    operator: "&&",
3894                    right: consequent.condition
3895                }),
3896                consequent: consequent.consequent,
3897                alternative: alternative
3898            });
3899        }
3900        // x ? y : y --> x, y
3901        if (consequent.equivalent_to(alternative)) {
3902            return make_node(AST_Seq, self, {
3903                car: self.condition,
3904                cdr: consequent
3905            }).optimize(compressor);
3906        }
3907
3908        if (is_true(self.consequent)) {
3909            if (is_false(self.alternative)) {
3910                // c ? true : false ---> !!c
3911                return booleanize(self.condition);
3912            }
3913            // c ? true : x ---> !!c || x
3914            return make_node(AST_Binary, self, {
3915                operator: "||",
3916                left: booleanize(self.condition),
3917                right: self.alternative
3918            });
3919        }
3920        if (is_false(self.consequent)) {
3921            if (is_true(self.alternative)) {
3922                // c ? false : true ---> !c
3923                return booleanize(self.condition.negate(compressor));
3924            }
3925            // c ? false : x ---> !c && x
3926            return make_node(AST_Binary, self, {
3927                operator: "&&",
3928                left: booleanize(self.condition.negate(compressor)),
3929                right: self.alternative
3930            });
3931        }
3932        if (is_true(self.alternative)) {
3933            // c ? x : true ---> !c || x
3934            return make_node(AST_Binary, self, {
3935                operator: "||",
3936                left: booleanize(self.condition.negate(compressor)),
3937                right: self.consequent
3938            });
3939        }
3940        if (is_false(self.alternative)) {
3941            // c ? x : false ---> !!c && x
3942            return make_node(AST_Binary, self, {
3943                operator: "&&",
3944                left: booleanize(self.condition),
3945                right: self.consequent
3946            });
3947        }
3948
3949        return self;
3950
3951        function booleanize(node) {
3952            if (node.is_boolean()) return node;
3953            // !!expression
3954            return make_node(AST_UnaryPrefix, node, {
3955                operator: "!",
3956                expression: node.negate(compressor)
3957            });
3958        }
3959
3960        // AST_True or !0
3961        function is_true(node) {
3962            return node instanceof AST_True
3963                || (node instanceof AST_UnaryPrefix
3964                    && node.operator == "!"
3965                    && node.expression instanceof AST_Constant
3966                    && !node.expression.value);
3967        }
3968        // AST_False or !1
3969        function is_false(node) {
3970            return node instanceof AST_False
3971                || (node instanceof AST_UnaryPrefix
3972                    && node.operator == "!"
3973                    && node.expression instanceof AST_Constant
3974                    && !!node.expression.value);
3975        }
3976    });
3977
3978    OPT(AST_Boolean, function(self, compressor){
3979        if (compressor.option("booleans")) {
3980            var p = compressor.parent();
3981            if (p instanceof AST_Binary && (p.operator == "=="
3982                                            || p.operator == "!=")) {
3983                compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
3984                    operator : p.operator,
3985                    value    : self.value,
3986                    file     : p.start.file,
3987                    line     : p.start.line,
3988                    col      : p.start.col,
3989                });
3990                return make_node(AST_Number, self, {
3991                    value: +self.value
3992                });
3993            }
3994            return make_node(AST_UnaryPrefix, self, {
3995                operator: "!",
3996                expression: make_node(AST_Number, self, {
3997                    value: 1 - self.value
3998                })
3999            });
4000        }
4001        return self;
4002    });
4003
4004    OPT(AST_Sub, function(self, compressor){
4005        var prop = self.property;
4006        if (prop instanceof AST_String && compressor.option("properties")) {
4007            prop = prop.getValue();
4008            if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) {
4009                return make_node(AST_Dot, self, {
4010                    expression : self.expression,
4011                    property   : prop
4012                }).optimize(compressor);
4013            }
4014            var v = parseFloat(prop);
4015            if (!isNaN(v) && v.toString() == prop) {
4016                self.property = make_node(AST_Number, self.property, {
4017                    value: v
4018                });
4019            }
4020        }
4021        var ev = self.evaluate(compressor);
4022        if (ev !== self) {
4023            ev = make_node_from_constant(ev, self).optimize(compressor);
4024            return best_of(compressor, ev, self);
4025        }
4026        return self;
4027    });
4028
4029    OPT(AST_Dot, function(self, compressor){
4030        var def = self.resolve_defines(compressor);
4031        if (def) {
4032            return def.optimize(compressor);
4033        }
4034        var prop = self.property;
4035        if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
4036            return make_node(AST_Sub, self, {
4037                expression : self.expression,
4038                property   : make_node(AST_String, self, {
4039                    value: prop
4040                })
4041            }).optimize(compressor);
4042        }
4043        if (compressor.option("unsafe_proto")
4044            && self.expression instanceof AST_Dot
4045            && self.expression.property == "prototype") {
4046            var exp = self.expression.expression;
4047            if (exp instanceof AST_SymbolRef && exp.undeclared()) switch (exp.name) {
4048              case "Array":
4049                self.expression = make_node(AST_Array, self.expression, {
4050                    elements: []
4051                });
4052                break;
4053              case "Object":
4054                self.expression = make_node(AST_Object, self.expression, {
4055                    properties: []
4056                });
4057                break;
4058              case "String":
4059                self.expression = make_node(AST_String, self.expression, {
4060                    value: ""
4061                });
4062                break;
4063            }
4064        }
4065        var ev = self.evaluate(compressor);
4066        if (ev !== self) {
4067            ev = make_node_from_constant(ev, self).optimize(compressor);
4068            return best_of(compressor, ev, self);
4069        }
4070        return self;
4071    });
4072
4073    function literals_in_boolean_context(self, compressor) {
4074        if (compressor.option("booleans") && compressor.in_boolean_context()) {
4075            return best_of(compressor, self, make_node(AST_Seq, self, {
4076                car: self,
4077                cdr: make_node(AST_True, self)
4078            }).optimize(compressor));
4079        }
4080        return self;
4081    };
4082    OPT(AST_Array, literals_in_boolean_context);
4083    OPT(AST_Object, literals_in_boolean_context);
4084    OPT(AST_RegExp, literals_in_boolean_context);
4085
4086    OPT(AST_Return, function(self, compressor){
4087        if (self.value && is_undefined(self.value, compressor)) {
4088            self.value = null;
4089        }
4090        return self;
4091    });
4092
4093    OPT(AST_VarDef, function(self, compressor){
4094        var defines = compressor.option("global_defs");
4095        if (defines && HOP(defines, self.name.name)) {
4096            compressor.warn('global_defs ' + self.name.name + ' redefined [{file}:{line},{col}]', self.start);
4097        }
4098        return self;
4099    });
4100
4101})();
4102