1 /***********************************************************************
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
6 -------------------------------- (C) ---------------------------------
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
12 Distributed under the BSD license:
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
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.
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
42 ***********************************************************************/
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,
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"]),
67 hoist_funs : !false_by_default,
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,
79 negate_iife : !false_by_default,
88 var pure_funcs = this.options["pure_funcs"];
89 if (typeof pure_funcs == "function") {
90 this.pure_funcs = pure_funcs;
92 this.pure_funcs = pure_funcs ? function(node) {
93 return pure_funcs.indexOf(node.expression.print_to_string()) < 0;
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);
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(/,/);
107 this.top_retain = function(def) {
108 return top_retain.indexOf(def.name) >= 0;
111 var sequences = this.options["sequences"];
112 this.sequences_limit = sequences == 1 ? 200 : sequences | 0;
113 this.warnings_produced = {};
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);
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);
129 if (this.option("expression")) {
130 node = node.process_expression(false);
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);
144 clear_warnings: function() {
145 this.warnings_produced = {};
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);
155 node = node.optimize(this);
156 if (was_scope && node instanceof AST_Scope) {
157 node.drop_unused(this);
160 node._squeezed = true;
167 function OPT(node, optimizer) {
168 node.DEFMETHOD("optimize", function(compressor){
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);
179 OPT(AST_Node, function(self, compressor){
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();
188 AST_Node.DEFMETHOD("process_expression", function(insert) {
190 var tt = new TreeTransformer(function(node) {
191 if (insert && node instanceof AST_SimpleStatement) {
192 return make_node(AST_Return, node, {
196 if (!insert && node instanceof AST_Return) {
197 return make_node(AST_SimpleStatement, node, {
198 body: node.value || make_node(AST_Undefined, node)
201 if (node instanceof AST_Lambda && node !== self) {
204 if (node instanceof AST_Block) {
205 var index = node.body.length - 1;
207 node.body[index] = node.body[index].transform(tt);
210 if (node instanceof AST_If) {
211 node.body = node.body.transform(tt);
212 if (node.alternative) {
213 node.alternative = node.alternative.transform(tt);
216 if (node instanceof AST_With) {
217 node.body = node.body.transform(tt);
221 return self.transform(tt);
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");
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);
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;
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)) {
251 if (ie8 && node instanceof AST_SymbolCatch) {
252 node.definition().fixed = false;
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);
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);
273 if (node instanceof AST_If || node instanceof AST_DWLoop) {
274 node.condition.walk(tw);
278 if (node.alternative) {
280 node.alternative.walk(tw);
285 if (node instanceof AST_LabeledStatement) {
291 if (node instanceof AST_For) {
292 if (node.init) node.init.walk(tw);
294 if (node.condition) node.condition.walk(tw);
296 if (node.step) node.step.walk(tw);
300 if (node instanceof AST_ForIn) {
301 node.init.walk(suppressor);
302 node.object.walk(tw);
308 if (node instanceof AST_Catch) {
318 function mark_as_safe(def) {
319 safe_ids[safe_ids.length - 1][def.id] = true;
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;
329 safe_ids.push(Object.create(null));
336 function reset_def(def) {
337 def.fixed = undefined;
339 def.should_replace = undefined;
342 function isModified(node, level) {
343 var parent = tw.parent(level);
344 if (isLHS(node, parent)
345 || parent instanceof AST_Call && parent.expression === node) {
347 } else if (parent instanceof AST_PropAccess && parent.expression === node) {
348 return isModified(parent, level + 1);
353 function make_node(ctor, orig, props) {
354 if (!props) props = {};
356 if (!props.start) props.start = orig.start;
357 if (!props.end) props.end = orig.end;
359 return new ctor(props);
362 function make_node_from_constant(compressor, val, orig) {
363 switch (typeof val) {
365 return make_node(AST_String, orig, {
370 return make_node(AST_NaN, orig);
374 return make_node(AST_UnaryPrefix, orig, {
376 expression: make_node(AST_Number, orig, { value: -val })
380 return make_node(AST_Number, orig, { value: val });
382 return make_node(val ? AST_True : AST_False, orig).transform(compressor);
384 return make_node(AST_Undefined, orig).transform(compressor);
387 return make_node(AST_Null, orig, { value: null });
389 if (val instanceof RegExp) {
390 return make_node(AST_RegExp, orig, { value: val });
392 throw new Error(string_template("Can't handle constant of type: {type}", {
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, {
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");
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;
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);
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;
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);
454 function tighten_body(statements, compressor) {
455 var CHANGED, max_iter = 10;
458 if (compressor.option("angular")) {
459 statements = process_for_angular(statements);
461 statements = eliminate_spurious_blocks(statements);
462 if (compressor.option("dead_code")) {
463 statements = eliminate_dead_code(statements, compressor);
465 if (compressor.option("if_return")) {
466 statements = handle_if_return(statements, compressor);
468 if (compressor.sequences_limit > 0) {
469 statements = sequencesize(statements, compressor);
471 if (compressor.option("join_vars")) {
472 statements = join_consecutive_vars(statements, compressor);
474 if (compressor.option("collapse_vars")) {
475 statements = collapse_single_use_vars(statements, compressor);
477 } while (CHANGED && max_iter-- > 0);
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.
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;
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);
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;
506 var var_names_seen = {};
507 var side_effects_encountered = false;
508 var lvalues_encountered = false;
511 // Scan variable definitions from right to left.
512 for (var var_defs_index = var_defs.length; --var_defs_index >= 0;) {
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;
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;
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;
530 var ref = def.references[0];
532 // Don't replace ref if eval() or with statement in scope.
533 if (ref.scope.uses_eval || ref.scope.uses_with) break;
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) {
539 && !ctt.find_parent(AST_ForIn)) {
540 return replace_var(node, ctt.parent(), true);
547 // Restrict var replacement to constants if side effects encountered.
548 if (side_effects_encountered |= lvalues_encountered) continue;
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);
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;
562 var_decl.value.walk(tw);
564 // Replace the non-constant single use var in statement if side effect free.
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;
584 function postorder(node) {
585 if (unwind) return node;
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;
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);
609 function is_lvalue(node, parent) {
610 return node instanceof AST_SymbolRef && isLHS(node, parent);
612 function replace_var(node, parent, is_constant) {
613 if (is_lvalue(node, parent)) return node;
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;
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;
624 // Further optimize statement after substitution.
625 stat.reset_opt_flags(compressor);
627 compressor.warn("Collapsing " + (is_constant ? "constant" : "variable") +
628 " " + var_name + " [{file}:{line},{col}]", node.start);
634 function process_for_angular(statements) {
635 function has_inject(comment) {
636 return /@ngInject/.test(comment.value);
638 function make_arguments_names_list(func) {
639 return func.argnames.map(function(sym){
640 return make_node(AST_String, sym, { value: sym.name });
643 function make_array(orig, elements) {
644 return make_node(AST_Array, orig, { elements: elements });
646 function make_injector(func, name) {
647 return make_node(AST_SimpleStatement, func, {
648 body: make_node(AST_Assign, func, {
650 left: make_node(AST_Dot, name, {
651 expression: make_node(AST_SymbolRef, name, name),
654 right: make_array(func, make_arguments_names_list(func))
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));
669 // if this is chained call check previous one recursively
670 if (body.expression && body.expression.expression) {
671 check_expression(body.expression.expression);
675 return statements.reduce(function(a, stat){
678 if (stat.body && stat.body.args) {
679 check_expression(stat.body);
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)) {
687 if (stat instanceof AST_Defun) {
688 a.push(make_injector(stat, stat.name));
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));
698 compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token);
708 function eliminate_spurious_blocks(statements) {
710 return statements.reduce(function(a, stat){
711 if (stat instanceof AST_BlockStatement) {
713 a.push.apply(a, eliminate_spurious_blocks(stat.body));
714 } else if (stat instanceof AST_EmptyStatement) {
716 } else if (stat instanceof AST_Directive) {
717 if (seen_dirs.indexOf(stat.value) < 0) {
719 seen_dirs.push(stat.value);
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];
738 case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
740 // note, ret.length is probably always zero
741 // because we drop unreachable code before this
742 // step. nevertheless, it's good to check.
744 case stat instanceof AST_If:
745 if (stat.body instanceof AST_Return) {
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) {
753 var cond = make_node(AST_SimpleStatement, stat.condition, {
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) {
764 stat.alternative = ret[0];
765 ret[0] = stat.transform(compressor);
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) {
774 stat.alternative = ret[0] || make_node(AST_Return, stat, {
777 ret[0] = stat.transform(compressor);
781 // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
782 if (!stat.body.value && in_lambda) {
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, {
791 stat.alternative = null;
792 ret = funs.concat([ stat.transform(compressor) ]);
797 // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
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
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) {
807 ret.push(make_node(AST_Return, ret[0], {
809 }).transform(compressor));
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))) {
821 remove(ab.label.thedef.references, ab);
824 var body = as_statement_array(stat.body).slice(0, -1);
826 stat.condition = stat.condition.negate(compressor);
827 stat.body = make_node(AST_BlockStatement, stat, {
828 body: as_statement_array(stat.alternative).concat(ret)
830 stat.alternative = make_node(AST_BlockStatement, stat, {
833 ret = [ stat.transform(compressor) ];
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))) {
843 remove(ab.label.thedef.references, ab);
847 stat.body = make_node(AST_BlockStatement, stat.body, {
848 body: as_statement_array(stat.body).concat(ret)
850 stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
851 body: as_statement_array(stat.alternative).slice(0, -1)
853 ret = [ stat.transform(compressor) ];
866 function has_multiple_if_returns(statements) {
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;
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){
884 extract_declarations_from_unreachable_code(compressor, stat, a);
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)) {
893 remove(stat.label.thedef.references, stat);
901 if (aborts(stat)) has_quit = true;
905 CHANGED = statements.length != orig;
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, {
919 statements.forEach(function(stat){
920 if (stat instanceof AST_SimpleStatement) {
921 if (seqLength(seq) >= compressor.sequences_limit) {
924 seq.push(seq.length > 0 ? statement_to_expression(stat) : stat.body);
931 ret = sequencesize_2(ret, compressor);
932 CHANGED = ret.length != statements.length;
936 function seqLength(a) {
937 for (var len = 0, i = 0; i < a.length; ++i) {
939 if (stat instanceof AST_Seq) {
948 function sequencesize_2(statements, compressor) {
949 function cons_seq(right) {
951 var left = prev.body;
952 if (left instanceof AST_Seq) {
955 left = AST_Seq.cons(left, right);
957 return left.transform(compressor);
959 var ret = [], prev = null;
960 statements.forEach(function(stat){
962 if (stat instanceof AST_For) {
965 prev.body.walk(new TreeWalker(function(node){
966 if (node instanceof AST_Binary && node.operator == "in")
969 if (stat.init && !(stat.init instanceof AST_Definitions)) {
970 stat.init = cons_seq(stat.init);
972 else if (!stat.init) {
973 stat.init = statement_to_expression(prev);
977 if (ex !== opera) throw ex;
980 else if (stat instanceof AST_If) {
981 stat.condition = cons_seq(stat.condition);
983 else if (stat instanceof AST_With) {
984 stat.expression = cons_seq(stat.expression);
986 else if (stat instanceof AST_Exit && stat.value) {
987 stat.value = cons_seq(stat.value);
989 else if (stat instanceof AST_Exit) {
990 stat.value = cons_seq(make_node(AST_Undefined, stat));
992 else if (stat instanceof AST_Switch) {
993 stat.expression = cons_seq(stat.expression);
997 prev = stat instanceof AST_SimpleStatement ? stat : null;
1002 function join_consecutive_vars(statements, compressor) {
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);
1009 else if (stat instanceof AST_For
1010 && prev instanceof AST_Var
1011 && (!stat.init || stat.init.TYPE == prev.TYPE)) {
1015 stat.init.definitions = prev.definitions.concat(stat.init.definitions);
1032 function extract_functions_from_statement_array(statements) {
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);
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);
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();
1055 if (node instanceof AST_Defun) {
1059 if (node instanceof AST_Scope) {
1065 function is_undefined(node) {
1066 return node instanceof AST_Undefined || node.is_undefined;
1069 /* -----[ boolean/negation helpers ]----- */
1071 // methods to determine whether an expression has a boolean result type
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);
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() );
1084 def(AST_Conditional, function(){
1085 return this.consequent.is_boolean() && this.alternative.is_boolean();
1087 def(AST_Assign, function(){
1088 return this.operator == "=" && this.right.is_boolean();
1090 def(AST_Seq, function(){
1091 return this.cdr.is_boolean();
1093 def(AST_True, return_true);
1094 def(AST_False, return_true);
1095 })(function(node, func){
1096 node.DEFMETHOD("is_boolean", func);
1099 // methods to determine if an expression has a numeric result type
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);
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);
1113 var assign = makePredicate("-= *= /= %= &= |= ^= <<= >>= >>>=");
1114 def(AST_Assign, function(compressor){
1115 return assign(this.operator) || this.right.is_number(compressor);
1117 def(AST_Seq, function(compressor){
1118 return this.cdr.is_number(compressor);
1120 def(AST_Conditional, function(compressor){
1121 return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
1123 })(function(node, func){
1124 node.DEFMETHOD("is_number", func);
1127 // methods to determine if an expression has a string result type
1129 def(AST_Node, return_false);
1130 def(AST_String, return_true);
1131 def(AST_UnaryPrefix, function(){
1132 return this.operator == "typeof";
1134 def(AST_Binary, function(compressor){
1135 return this.operator == "+" &&
1136 (this.left.is_string(compressor) || this.right.is_string(compressor));
1138 def(AST_Assign, function(compressor){
1139 return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
1141 def(AST_Seq, function(compressor){
1142 return this.cdr.is_string(compressor);
1144 def(AST_Conditional, function(compressor){
1145 return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
1147 })(function(node, func){
1148 node.DEFMETHOD("is_string", func);
1151 function isLHS(node, parent) {
1152 return parent instanceof AST_Unary && (parent.operator == "++" || parent.operator == "--")
1153 || parent instanceof AST_Assign && parent.left === node;
1157 AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
1158 if (!compressor.option("global_defs")) return;
1159 var def = this._find_defs(compressor, "");
1161 var node, parent = this, level = 0;
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);
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);
1180 if (value && typeof value == "object") {
1182 for (var key in value) {
1183 props.push(make_node(AST_ObjectKeyVal, orig, {
1185 value: to_node(compressor, value[key], orig)
1188 return make_node(AST_Object, orig, {
1192 return make_node_from_constant(compressor, value, orig);
1194 def(AST_Node, noop);
1195 def(AST_Dot, function(compressor, suffix){
1196 return this.expression._find_defs(compressor, suffix + "." + this.property);
1198 def(AST_SymbolRef, function(compressor, suffix){
1199 if (!this.global()) return;
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) {
1208 node.thedef = top.def_global(node);
1214 })(function(node, func){
1215 node.DEFMETHOD("_find_defs", func);
1218 function best_of(ast1, ast2) {
1219 return ast1.print_to_string().length >
1220 ast2.print_to_string().length
1224 function best_of_statement(ast1, ast2) {
1225 return best_of(make_node(AST_SimpleStatement, ast1, {
1227 }), make_node(AST_SimpleStatement, ast2, {
1232 // methods to evaluate a constant expression
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 ];
1245 val = this._eval(compressor);
1247 if (ex !== def) throw ex;
1252 node = make_node_from_constant(compressor, val, this);
1256 return [ best_of(node, this), val ];
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);
1265 return this instanceof AST_UnaryPrefix
1266 && this.expression instanceof AST_Constant
1267 && unaryPrefix(this.operator);
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)) {
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) {
1281 return !this.expression.value;
1283 return ~this.expression.value;
1285 return -this.expression.value;
1287 return +this.expression.value;
1289 throw new Error(string_template("Cannot evaluate unary expression {value}", {
1290 value: this.print_to_string()
1293 var result = this.evaluate(compressor);
1294 if (result.length > 1) {
1297 throw new Error(string_template("Cannot evaluate constant [{file}:{line},{col}]", this.start));
1299 def(AST_Statement, function(){
1300 throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
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(){
1309 def(AST_Function, function(){
1312 function ev(node, compressor) {
1313 if (!compressor) throw new Error("Compressor must be passed");
1315 return node._eval(compressor);
1317 def(AST_Node, function(){
1318 throw def; // not constant
1320 def(AST_Constant, function(){
1321 return this.getValue();
1323 def(AST_Array, function(compressor){
1324 if (compressor.option("unsafe")) {
1325 return this.elements.map(function(element) {
1326 return ev(element, compressor);
1331 def(AST_Object, function(compressor){
1332 if (compressor.option("unsafe")) {
1334 for (var i = 0, len = this.properties.length; i < len; i++) {
1335 var prop = this.properties[i];
1337 if (key instanceof AST_Symbol) {
1339 } else if (key instanceof AST_Node) {
1340 key = ev(key, compressor);
1342 if (typeof Object.prototype[key] === 'function') {
1345 val[key] = ev(prop.value, compressor);
1351 def(AST_UnaryPrefix, function(compressor){
1352 var e = this.expression;
1353 switch (this.operator) {
1354 case "!": return !ev(e, compressor);
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(){};
1360 e = ev(e, compressor);
1362 // typeof <RegExp> returns "object" or "function" on different platforms
1363 // so cannot evaluate reliably
1364 if (e instanceof RegExp) throw def;
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);
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;
1401 if (isNaN(result) && c.find_parent(AST_With)) {
1402 // leave original expression as is
1407 def(AST_Conditional, function(compressor){
1408 return ev(this.condition, compressor)
1409 ? ev(this.consequent, compressor)
1410 : ev(this.alternative, compressor);
1412 def(AST_SymbolRef, function(compressor){
1413 if (this._evaluating) throw def;
1414 this._evaluating = true;
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);
1422 return d.fixed._evaluated;
1424 return ev(d.fixed, compressor);
1427 this._evaluating = false;
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);
1437 var val = ev(this.expression, compressor);
1438 if (val && HOP(val, key)) {
1444 })(function(node, func){
1445 node.DEFMETHOD("_eval", func);
1448 // method to negate an expression
1450 function basic_negation(exp) {
1451 return make_node(AST_UnaryPrefix, exp, {
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, {
1462 return best_of(negated, stat) === stat ? alt : negated;
1464 return best_of(negated, alt);
1466 def(AST_Node, function(){
1467 return basic_negation(this);
1469 def(AST_Statement, function(){
1470 throw new Error("Cannot negate a statement");
1472 def(AST_Function, function(){
1473 return basic_negation(this);
1475 def(AST_UnaryPrefix, function(){
1476 if (this.operator == "!")
1477 return this.expression;
1478 return basic_negation(this);
1480 def(AST_Seq, function(compressor){
1481 var self = this.clone();
1482 self.cdr = self.cdr.negate(compressor);
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);
1491 def(AST_Binary, function(compressor, first_in_statement){
1492 var self = this.clone(), op = this.operator;
1493 if (compressor.option("unsafe_comps")) {
1495 case "<=" : self.operator = ">" ; return self;
1496 case "<" : self.operator = ">=" ; return self;
1497 case ">=" : self.operator = "<" ; return self;
1498 case ">" : self.operator = "<=" ; return self;
1502 case "==" : self.operator = "!="; return self;
1503 case "!=" : self.operator = "=="; return self;
1504 case "===": self.operator = "!=="; return self;
1505 case "!==": self.operator = "==="; return self;
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);
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);
1517 return basic_negation(this);
1519 })(function(node, func){
1520 node.DEFMETHOD("negate", function(compressor, first_in_statement){
1521 return func.call(this, compressor, first_in_statement);
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;
1529 var comments, last_comment;
1531 && (comments = this.start.comments_before)
1533 && /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) {
1534 pure = last_comment;
1536 return this.pure = pure;
1539 // determine if expression has side effects
1541 def(AST_Node, return_true);
1543 def(AST_EmptyStatement, return_false);
1544 def(AST_Constant, return_false);
1545 def(AST_This, return_false);
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))
1556 def(AST_Block, function(compressor){
1557 for (var i = this.body.length; --i >= 0;) {
1558 if (this.body[i].has_side_effects(compressor))
1564 def(AST_SimpleStatement, function(compressor){
1565 return this.body.has_side_effects(compressor);
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);
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);
1579 def(AST_Unary, function(compressor){
1580 return this.operator == "delete"
1581 || this.operator == "++"
1582 || this.operator == "--"
1583 || this.expression.has_side_effects(compressor);
1585 def(AST_SymbolRef, function(compressor){
1586 return this.global() && this.undeclared();
1588 def(AST_Object, function(compressor){
1589 for (var i = this.properties.length; --i >= 0;)
1590 if (this.properties[i].has_side_effects(compressor))
1594 def(AST_ObjectProperty, function(compressor){
1595 return this.value.has_side_effects(compressor);
1597 def(AST_Array, function(compressor){
1598 for (var i = this.elements.length; --i >= 0;)
1599 if (this.elements[i].has_side_effects(compressor))
1603 def(AST_Dot, function(compressor){
1604 if (!compressor.option("pure_getters")) return true;
1605 return this.expression.has_side_effects(compressor);
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);
1612 def(AST_PropAccess, function(compressor){
1613 return !compressor.option("pure_getters");
1615 def(AST_Seq, function(compressor){
1616 return this.car.has_side_effects(compressor)
1617 || this.cdr.has_side_effects(compressor);
1619 })(function(node, func){
1620 node.DEFMETHOD("has_side_effects", func);
1623 // tell me if a statement aborts
1624 function aborts(thing) {
1625 return thing && thing.aborts();
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]);
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;
1639 })(function(node, func){
1640 node.DEFMETHOD("aborts", func);
1643 /* -----[ optimizers ]----- */
1645 OPT(AST_Directive, function(self, compressor){
1646 if (compressor.has_directive(self.value) === "up") {
1647 return make_node(AST_EmptyStatement, self);
1652 OPT(AST_Debugger, function(self, compressor){
1653 if (compressor.option("drop_debugger"))
1654 return make_node(AST_EmptyStatement, self);
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);
1663 return self.label.references.length == 0 ? self.body : self;
1666 OPT(AST_Block, function(self, compressor){
1667 self.body = tighten_body(self.body, compressor);
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);
1680 AST_Scope.DEFMETHOD("drop_unused", function(compressor){
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)
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;
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;
1704 var initializations = new Dictionary();
1705 // pass 1: find out which symbols are directly used in
1706 // this scope (not in nested scopes).
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);
1718 initializations.add(node.name.name, node);
1719 return true; // don't go in nested scopes
1721 if (node instanceof AST_Definitions && scope === self) {
1722 node.definitions.forEach(function(def){
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);
1731 initializations.add(def.name.name, def.value);
1732 if (def.value.has_side_effects(compressor)) {
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);
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);
1755 if (node instanceof AST_Scope) {
1756 var save_scope = scope;
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);
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
1791 && !compressor.option("keep_fnames")
1792 && !(node.name.definition().id in in_use_ids)) {
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;) {
1799 if (!(sym.definition().id in in_use_ids)) {
1801 compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
1803 file : sym.start.file,
1804 line : sym.start.line,
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
1820 return make_node(AST_EmptyStatement, node);
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;
1828 name : def.name.name,
1829 file : def.name.start.file,
1830 line : def.name.start.line,
1831 col : def.name.start.col
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);
1837 compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w);
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;
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;) {
1852 if (x._unused_side_effects) {
1853 side_effects.push(x._unused_side_effects);
1856 if (side_effects.length > 0) {
1857 side_effects.push(x.value);
1858 x.value = AST_Seq.from_array(side_effects);
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)
1871 side_effects = null;
1873 if (def.length == 0 && !side_effects) {
1874 return make_node(AST_EmptyStatement, node);
1876 if (def.length == 0) {
1877 return in_list ? MAP.splice(side_effects.body) : side_effects;
1879 node.definitions = def;
1881 side_effects.body.unshift(node);
1882 return in_list ? MAP.splice(side_effects.body) : side_effects;
1886 if (drop_vars && assign_as_unused) {
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;
1896 if (n !== node) return n;
1898 if (node instanceof AST_For) {
1899 descend(node, this);
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`.
1907 var body = node.init.body.slice(0, -1);
1908 node.init = node.init.body.slice(-1)[0].body;
1911 return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
1916 if (node instanceof AST_Scope && node !== self)
1924 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
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) {
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)
1938 if (node instanceof AST_Var) {
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) {
1949 return make_node(AST_EmptyStatement, node);
1951 if (node instanceof AST_Defun && hoist_funs) {
1953 return make_node(AST_EmptyStatement, node);
1955 if (node instanceof AST_Var && hoist_vars) {
1956 node.definitions.forEach(function(def){
1957 vars.set(def.name.name, def);
1960 var seq = node.to_assignments();
1961 var p = tt.parent();
1962 if (p instanceof AST_ForIn && p.init === node) {
1964 var def = node.definitions[0].name;
1965 return make_node(AST_SymbolRef, def, def);
1969 if (p instanceof AST_For && p.init === node) {
1972 if (!seq) return make_node(AST_EmptyStatement, node);
1973 return make_node(AST_SimpleStatement, node, {
1977 if (node instanceof AST_Scope)
1978 return node; // to avoid descending in nested scopes
1982 self = self.transform(tt);
1983 if (vars_found > 0) {
1984 // collect only vars which don't show up in self's arguments list
1986 vars.each(function(def, name){
1987 if (self instanceof AST_Lambda
1988 && find_if(function(x){ return x.name == def.name.name },
1995 vars.set(name, def);
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))
2008 var def = vars.get(sym.name);
2009 if (def.value) break;
2010 def.value = expr.right;
2013 self.body.splice(i, 1);
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))
2022 var def = vars.get(sym.name);
2023 if (def.value) break;
2024 def.value = assign.right;
2027 self.body[i].body = expr.cdr;
2031 if (self.body[i] instanceof AST_EmptyStatement) {
2032 self.body.splice(i, 1);
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);
2042 defs = make_node(AST_Var, self, {
2048 self.body = dirs.concat(hoisted, self.body);
2053 // drop_side_effect_free()
2054 // remove side-effect-free parts which only affects return value
2056 function return_this() {
2060 function return_null() {
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];
2075 first_in_statement = false;
2078 return changed ? ret.length ? ret : null : nodes;
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);
2094 compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
2095 this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
2097 var args = trim(this.args, compressor, first_in_statement);
2098 return args && AST_Seq.from_array(args);
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) {
2107 var node = this.clone();
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, {
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, {
2126 left: this.condition,
2128 }) : this.condition.drop_side_effect_free(compressor);
2129 if (!alternative) return make_node(AST_Binary, this, {
2131 left: this.condition,
2134 var node = this.clone();
2135 node.consequent = consequent;
2136 node.alternative = alternative;
2139 def(AST_Unary, function(compressor, first_in_statement){
2140 switch (this.operator) {
2146 if (this.expression instanceof AST_SymbolRef) return null;
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
2161 def(AST_SymbolRef, function() {
2162 return this.undeclared() ? this : null;
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);
2168 def(AST_ObjectProperty, function(compressor, first_in_statement){
2169 return this.value.drop_side_effect_free(compressor, first_in_statement);
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);
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);
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, {
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, {
2199 })(function(node, func){
2200 node.DEFMETHOD("drop_side_effect_free", func);
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);
2208 compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
2209 return make_node(AST_EmptyStatement, self);
2211 if (node !== body) {
2212 return make_node(AST_SimpleStatement, self, { body: node });
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) {
2224 return make_node(AST_For, self, {
2227 } else if (self instanceof AST_While) {
2228 if (compressor.option("dead_code")) {
2230 extract_declarations_from_unreachable_code(compressor, self.body, a);
2231 return make_node(AST_BlockStatement, self, { body: a });
2234 // self instanceof AST_Do
2238 if (self instanceof AST_While) {
2239 return make_node(AST_For, self, self).transform(compressor);
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);
2252 self.body = make_node(AST_BlockStatement, self.body, {
2254 }).transform(compressor);
2256 if_break_in_loop(self, compressor);
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,
2266 right: first.condition.negate(compressor),
2269 self.condition = first.condition.negate(compressor);
2271 drop_it(first.alternative);
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,
2279 right: first.condition,
2282 self.condition = first.condition;
2284 drop_it(first.body);
2289 OPT(AST_For, function(self, compressor){
2290 var cond = self.condition;
2292 cond = cond.evaluate(compressor);
2293 self.condition = cond[0];
2295 if (!compressor.option("loops")) return self;
2297 if (cond.length > 1 && !cond[1]) {
2298 if (compressor.option("dead_code")) {
2300 if (self.init instanceof AST_Statement) {
2303 else if (self.init) {
2304 a.push(make_node(AST_SimpleStatement, self.init, {
2308 extract_declarations_from_unreachable_code(compressor, self.body, a);
2309 return make_node(AST_BlockStatement, self, { body: a });
2313 if_break_in_loop(self, compressor);
2317 OPT(AST_If, function(self, compressor){
2318 if (is_empty(self.alternative)) self.alternative = null;
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) {
2329 compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
2330 if (compressor.option("dead_code")) {
2332 if (self.alternative) {
2333 extract_declarations_from_unreachable_code(compressor, self.alternative, a);
2336 return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
2339 compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
2340 if (compressor.option("dead_code")) {
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);
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;
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);
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)
2374 }).transform(compressor);
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;
2384 if (negated_is_best) return make_node(AST_SimpleStatement, self, {
2385 body: make_node(AST_Binary, self, {
2388 right : statement_to_expression(self.body)
2390 }).transform(compressor);
2391 return make_node(AST_SimpleStatement, self, {
2392 body: make_node(AST_Binary, self, {
2394 left : self.condition,
2395 right : statement_to_expression(self.body)
2397 }).transform(compressor);
2399 if (self.body instanceof AST_EmptyStatement
2401 && self.alternative instanceof AST_SimpleStatement) {
2402 return make_node(AST_SimpleStatement, self, {
2403 body: make_node(AST_Binary, self, {
2405 left : self.condition,
2406 right : statement_to_expression(self.alternative)
2408 }).transform(compressor);
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)
2419 }).transform(compressor);
2421 if (self.body instanceof AST_If
2422 && !self.body.alternative
2423 && !self.alternative) {
2424 self.condition = make_node(AST_Binary, self.condition, {
2426 left: self.condition,
2427 right: self.body.condition
2428 }).transform(compressor);
2429 self.body = self.body.body;
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, {
2437 }).transform(compressor);
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);
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);
2459 var last_branch = self.body[self.body.length - 1];
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) {
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;
2478 var in_block = false;
2479 var started = false;
2480 var stopped = 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
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);
2494 }).transform(compressor);
2496 else if (node instanceof AST_If || node instanceof AST_Try) {
2499 descend(node, this);
2503 else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) {
2504 var save = in_block;
2506 descend(node, this);
2510 else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
2515 if (in_block) return node;
2517 return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
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
2527 if (exp[1] === value || started) {
2529 if (aborts(node)) stopped = true;
2530 descend(node, this);
2535 descend(node, this);
2539 tt.stack = compressor.stack.slice(); // so that's able to see parent nodes
2540 self = self.transform(tt);
2542 if (ex !== self) throw ex;
2547 OPT(AST_Case, function(self, compressor){
2548 self.body = tighten_body(self.body, compressor);
2552 OPT(AST_Try, function(self, compressor){
2553 self.body = tighten_body(self.body, compressor);
2557 AST_Definitions.DEFMETHOD("remove_initializers", function(){
2558 this.definitions.forEach(function(def){ def.value = null });
2561 AST_Definitions.DEFMETHOD("to_assignments", function(){
2562 var assignments = this.definitions.reduce(function(a, def){
2564 var name = make_node(AST_SymbolRef, def.name, def.name);
2565 a.push(make_node(AST_Assign, def, {
2573 if (assignments.length == 0) return null;
2574 return AST_Seq.from_array(assignments);
2577 OPT(AST_Definitions, function(self, compressor){
2578 if (self.definitions.length == 0)
2579 return make_node(AST_EmptyStatement, self);
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
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);
2594 self.args[end++] = node;
2597 self.args.length = end;
2599 if (compressor.option("unsafe")) {
2600 if (exp instanceof AST_SymbolRef && exp.undeclared()) {
2603 if (self.args.length != 1) {
2604 return make_node(AST_Array, self, {
2606 }).transform(compressor);
2610 if (self.args.length == 0) {
2611 return make_node(AST_Object, self, {
2617 if (self.args.length == 0) return make_node(AST_String, self, {
2620 if (self.args.length <= 1) return make_node(AST_Binary, self, {
2623 right: make_node(AST_String, self, { value: "" })
2624 }).transform(compressor);
2627 if (self.args.length == 0) return make_node(AST_Number, self, {
2630 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
2631 expression: self.args[0],
2633 }).transform(compressor);
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],
2642 }).transform(compressor);
2645 // new Function() => function(){}
2646 if (self.args.length == 0) return make_node(AST_Function, self, {
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.
2655 var code = "(function(" + self.args.slice(0, -1).map(function(arg){
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") });
2666 ast.walk(new TreeWalker(function(node){
2667 if (node instanceof AST_Lambda) {
2673 if (ex !== ast) throw ex;
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()
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], {
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());
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: "" }),
2706 right: exp.expression
2707 }).transform(compressor);
2709 else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
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];
2718 exp.expression.elements.forEach(function(el) {
2719 el = el.evaluate(compressor);
2720 if (el.length > 1) {
2723 if (consts.length > 0) {
2724 elements.push(make_node(AST_String, self, {
2725 value: consts.join(separator)
2729 elements.push(el[0]);
2732 if (consts.length > 0) {
2733 elements.push(make_node(AST_String, self, {
2734 value: consts.join(separator)
2737 if (elements.length == 0) return make_node(AST_String, self, { value: "" });
2738 if (elements.length == 1) {
2739 if (elements[0].is_string(compressor)) {
2742 return make_node(AST_Binary, elements[0], {
2744 left : make_node(AST_String, self, { value: "" }),
2748 if (separator == "") {
2750 if (elements[0].is_string(compressor)
2751 || elements[1].is_string(compressor)) {
2752 first = elements.shift();
2754 first = make_node(AST_String, self, { value: "" });
2756 return elements.reduce(function(prev, el){
2757 return make_node(AST_Binary, el, {
2762 }, first).transform(compressor);
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);
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);
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);
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;
2794 if (name instanceof AST_SymbolRef
2795 && name.name == "console"
2796 && name.undeclared()) {
2797 return make_node(AST_Undefined, self).transform(compressor);
2801 if (compressor.option("negate_iife")
2802 && compressor.parent() instanceof AST_SimpleStatement
2803 && is_iife_call(self)) {
2804 return self.negate(compressor, true);
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()) {
2819 return make_node(AST_Call, self, self).transform(compressor);
2826 OPT(AST_Seq, function(self, compressor){
2827 if (!compressor.option("side_effects"))
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")) {
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;
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,
2850 parent[field] = car;
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";
2866 if (is_undefined(self.cdr)) {
2867 return make_node(AST_UnaryPrefix, self, {
2869 expression : self.car
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();
2882 seq = AST_Seq.from_array(x).transform(compressor);
2889 OPT(AST_UnaryPostfix, function(self, compressor){
2890 return self.lift_sequences(compressor);
2893 OPT(AST_UnaryPrefix, function(self, compressor){
2894 var seq = self.lift_sequences(compressor);
2898 var e = self.expression;
2899 if (compressor.option("side_effects") && self.operator == "void") {
2900 e = e.drop_side_effect_free(compressor);
2902 self.expression = e;
2905 return make_node(AST_Undefined, self).transform(compressor);
2908 if (compressor.option("booleans") && compressor.in_boolean_context()) {
2909 switch (self.operator) {
2911 if (e instanceof AST_UnaryPrefix && e.operator == "!") {
2912 // !!foo ==> foo, if we're in boolean context
2913 return e.expression;
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));
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, {
2926 cdr: make_node(AST_True, self)
2927 }).optimize(compressor);
2930 return self.evaluate(compressor)[0];
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;
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();
2948 seq = AST_Seq.from_array(x).transform(compressor);
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();
2958 seq = AST_Seq.from_array(x).transform(compressor);
2965 var commutativeOperators = makePredicate("== === != !== * & | ^");
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,
2976 }).optimize(compressor);
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);
2984 function reverse(op) {
2986 if (op) self.operator = op;
2987 var tmp = self.left;
2988 self.left = self.right;
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.
2999 if (!(self.left instanceof AST_Binary
3000 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
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);
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;
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);
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;
3031 self = self.lift_sequences(compressor);
3032 if (compressor.option("comparisons")) switch (self.operator) {
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);
3040 // XXX: intentionally falling down to the next 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")) {
3052 self.left = make_node(AST_Undefined, self.left).optimize(compressor);
3053 if (self.operator.length == 2) self.operator += "=";
3058 if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
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, {
3066 cdr: make_node(AST_False, self)
3067 }).optimize(compressor);
3069 if (ll.length > 1 && ll[1]) {
3072 if (rr.length > 1 && rr[1]) {
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, {
3083 cdr: make_node(AST_True, self)
3084 }).optimize(compressor);
3086 if (ll.length > 1 && !ll[1]) {
3089 if (rr.length > 1 && !rr[1]) {
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, {
3100 cdr: make_node(AST_True, self)
3101 }).optimize(compressor);
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, {
3107 cdr: make_node(AST_True, self)
3108 }).optimize(compressor);
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, {
3118 expression: self.negate(compressor, statement)
3120 self = (statement ? best_of_statement : best_of)(self, negated);
3122 if (compressor.option("unsafe_comps")) {
3123 switch (self.operator) {
3124 case "<": reverse(">"); break;
3125 case "<=": reverse(">="); break;
3129 if (self.operator == "+") {
3130 if (self.right instanceof AST_String
3131 && self.right.getValue() == ""
3132 && self.left.is_string(compressor)) {
3135 if (self.left instanceof AST_String
3136 && self.left.getValue() == ""
3137 && self.right.is_string(compressor)) {
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);
3149 if (compressor.option("evaluate")) {
3150 switch (self.operator) {
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);
3157 compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
3158 return maintain_this_binding(compressor.parent(), self, self.left);
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);
3168 compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
3169 return maintain_this_binding(compressor.parent(), self, self.right);
3174 var associative = true;
3175 switch (self.operator) {
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, {
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
3190 right: self.right.right
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, {
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,
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, {
3220 left: make_node(AST_Binary, self.left, {
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
3229 right: self.right.right
3233 if (self.right instanceof AST_UnaryPrefix
3234 && self.right.operator == "-"
3235 && self.left.is_number(compressor)) {
3236 self = make_node(AST_Binary, self, {
3239 right: self.right.expression
3243 if (self.left instanceof AST_UnaryPrefix
3244 && self.left.operator == "-"
3246 && self.right.is_number(compressor)) {
3247 self = make_node(AST_Binary, self, {
3250 right: self.left.expression
3254 associative = compressor.option("unsafe_math");
3259 if (self.left.is_number(compressor)
3260 && self.right.is_number(compressor)
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,
3270 if (self.right instanceof AST_Constant
3271 && !(self.left instanceof AST_Constant)) {
3272 self = best_of(reversed, self);
3274 self = best_of(self, reversed);
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,
3286 right: self.right.left,
3287 start: self.left.start,
3288 end: self.right.left.end
3290 right: self.right.right
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,
3305 start: self.left.left.start,
3308 right: self.left.right
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,
3317 start: self.left.right.start,
3320 right: self.left.left
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
3342 right: self.left.left
3344 right: self.right.right
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))))))
3363 self.left = make_node(AST_Binary, self.left, {
3364 operator : self.operator,
3366 right : self.right.left
3368 self.right = self.right.right;
3369 return self.transform(compressor);
3371 return self.evaluate(compressor)[0];
3374 OPT(AST_SymbolRef, function(self, compressor){
3375 var def = self.resolve_defines(compressor);
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) {
3386 return make_node(AST_Undefined, self).transform(compressor);
3388 return make_node(AST_NaN, self).transform(compressor);
3390 return make_node(AST_Infinity, self).transform(compressor);
3393 if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
3394 var d = self.definition();
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;
3405 d.should_replace = false;
3408 if (d.should_replace) {
3409 return d.should_replace;
3416 OPT(AST_Infinity, function (self, compressor) {
3417 return make_node(AST_Binary, self, {
3419 left : make_node(AST_Number, self, {value: 1}),
3420 right : make_node(AST_Number, self, {value: 0})
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");
3429 var ref = make_node(AST_SymbolRef, self, {
3434 ref.is_undefined = true;
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;
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;
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);
3473 var cond = self.condition.evaluate(compressor);
3474 if (cond.length > 1) {
3476 compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
3477 return maintain_this_binding(compressor.parent(), self, self.consequent);
3479 compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
3480 return maintain_this_binding(compressor.parent(), self, self.alternative);
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, {
3488 consequent: self.alternative,
3489 alternative: self.consequent
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))
3503 * if (foo) exp = something; else exp = something_else;
3505 * exp = foo ? something : something_else;
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
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]
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,
3538 right: consequent.condition
3540 consequent: consequent.consequent,
3541 alternative: alternative
3544 // x ? y : y --> x, y
3545 if (consequent.equivalent_to(alternative)) {
3546 return make_node(AST_Seq, self, {
3547 car: self.condition,
3549 }).optimize(compressor);
3552 if (is_true(self.consequent)) {
3553 if (is_false(self.alternative)) {
3554 // c ? true : false ---> !!c
3555 return booleanize(self.condition);
3557 // c ? true : x ---> !!c || x
3558 return make_node(AST_Binary, self, {
3560 left: booleanize(self.condition),
3561 right: self.alternative
3564 if (is_false(self.consequent)) {
3565 if (is_true(self.alternative)) {
3566 // c ? false : true ---> !c
3567 return booleanize(self.condition.negate(compressor));
3569 // c ? false : x ---> !c && x
3570 return make_node(AST_Binary, self, {
3572 left: booleanize(self.condition.negate(compressor)),
3573 right: self.alternative
3576 if (is_true(self.alternative)) {
3577 // c ? x : true ---> !c || x
3578 return make_node(AST_Binary, self, {
3580 left: booleanize(self.condition.negate(compressor)),
3581 right: self.consequent
3584 if (is_false(self.alternative)) {
3585 // c ? x : false ---> !!c && x
3586 return make_node(AST_Binary, self, {
3588 left: booleanize(self.condition),
3589 right: self.consequent
3595 function booleanize(node) {
3596 if (node.is_boolean()) return node;
3598 return make_node(AST_UnaryPrefix, node, {
3600 expression: node.negate(compressor)
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);
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);
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,
3630 file : p.start.file,
3631 line : p.start.line,
3634 return make_node(AST_Number, self, {
3638 return make_node(AST_UnaryPrefix, self, {
3640 expression: make_node(AST_Number, self, {
3641 value: 1 - self.value
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,
3656 }).optimize(compressor);
3658 var v = parseFloat(prop);
3659 if (!isNaN(v) && v.toString() == prop) {
3660 self.property = make_node(AST_Number, self.property, {
3665 return self.evaluate(compressor)[0];
3668 OPT(AST_Dot, function(self, compressor){
3669 var def = self.resolve_defines(compressor);
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, {
3680 }).optimize(compressor);
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) {
3688 self.expression = make_node(AST_Array, self.expression, {
3693 self.expression = make_node(AST_Object, self.expression, {
3698 self.expression = make_node(AST_String, self.expression, {
3704 return self.evaluate(compressor)[0];
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, {
3712 cdr: make_node(AST_True, self)
3713 }).optimize(compressor));
3717 OPT(AST_Array, literals_in_boolean_context);
3718 OPT(AST_Object, literals_in_boolean_context);
3719 OPT(AST_RegExp, literals_in_boolean_context);
3721 OPT(AST_Return, function(self, compressor){
3722 if (self.value && is_undefined(self.value)) {
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);