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