1 /***********************************************************************
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS
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 annotations : !false_by_default,
52 arguments : !false_by_default,
53 arrows : !false_by_default,
54 assignments : !false_by_default,
55 awaits : !false_by_default,
56 booleans : !false_by_default,
57 collapse_vars : !false_by_default,
58 comparisons : !false_by_default,
59 conditionals : !false_by_default,
60 dead_code : !false_by_default,
61 default_values : !false_by_default,
62 directives : !false_by_default,
64 drop_debugger : !false_by_default,
65 evaluate : !false_by_default,
67 functions : !false_by_default,
69 hoist_exports : !false_by_default,
71 hoist_props : !false_by_default,
74 if_return : !false_by_default,
75 imports : !false_by_default,
76 inline : !false_by_default,
77 join_vars : !false_by_default,
78 keep_fargs : false_by_default,
80 keep_infinity : false,
81 loops : !false_by_default,
82 merge_vars : !false_by_default,
83 negate_iife : !false_by_default,
84 objects : !false_by_default,
85 optional_chains : !false_by_default,
87 properties : !false_by_default,
89 pure_getters : !false_by_default && "strict",
90 reduce_funcs : !false_by_default,
91 reduce_vars : !false_by_default,
92 rests : !false_by_default,
93 sequences : !false_by_default,
94 side_effects : !false_by_default,
95 spreads : !false_by_default,
96 strings : !false_by_default,
97 switches : !false_by_default,
98 templates : !false_by_default,
100 toplevel : !!(options && options["top_retain"]),
101 typeofs : !false_by_default,
103 unsafe_comps : false,
104 unsafe_Function : false,
106 unsafe_proto : false,
107 unsafe_regexp : false,
108 unsafe_undefined: false,
109 unused : !false_by_default,
110 varify : !false_by_default,
112 yields : !false_by_default,
114 var evaluate = this.options["evaluate"];
115 this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
116 var global_defs = this.options["global_defs"];
117 if (typeof global_defs == "object") for (var key in global_defs) {
118 if (/^@/.test(key) && HOP(global_defs, key)) {
119 global_defs[key.slice(1)] = parse(global_defs[key], {
124 if (this.options["inline"] === true) this.options["inline"] = 3;
125 this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) {
126 if (lambda.length_read) return false;
127 var name = lambda.name;
128 if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
129 if (name.fixed_value() !== lambda) return false;
130 var def = name.definition();
131 if (def.direct_access) return false;
132 var escaped = def.escaped;
133 return escaped && escaped.depth != 1;
135 var pure_funcs = this.options["pure_funcs"];
136 if (typeof pure_funcs == "function") {
137 this.pure_funcs = pure_funcs;
138 } else if (typeof pure_funcs == "string") {
139 this.pure_funcs = function(node) {
141 if (node instanceof AST_Call) {
142 expr = node.expression;
143 } else if (node instanceof AST_Template) {
146 return !(expr && pure_funcs === expr.print_to_string());
148 } else if (Array.isArray(pure_funcs)) {
149 this.pure_funcs = function(node) {
151 if (node instanceof AST_Call) {
152 expr = node.expression;
153 } else if (node instanceof AST_Template) {
156 return !(expr && member(expr.print_to_string(), pure_funcs));
159 this.pure_funcs = return_true;
161 var sequences = this.options["sequences"];
162 this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
163 var top_retain = this.options["top_retain"];
164 if (top_retain instanceof RegExp) {
165 this.top_retain = function(def) {
166 return top_retain.test(def.name);
168 } else if (typeof top_retain == "function") {
169 this.top_retain = top_retain;
170 } else if (top_retain) {
171 if (typeof top_retain == "string") {
172 top_retain = top_retain.split(/,/);
174 this.top_retain = function(def) {
175 return member(def.name, top_retain);
178 var toplevel = this.options["toplevel"];
179 this.toplevel = typeof toplevel == "string" ? {
180 funcs: /funcs/.test(toplevel),
181 vars: /vars/.test(toplevel)
188 Compressor.prototype = new TreeTransformer;
189 merge(Compressor.prototype, {
190 option: function(key) { return this.options[key] },
191 exposed: function(def) {
192 if (def.exported) return true;
193 if (def.undeclared) return true;
194 if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
195 var toplevel = this.toplevel;
196 return !all(def.orig, function(sym) {
197 return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
200 compress: function(node) {
201 node = node.resolve_defines(this);
202 node.hoist_exports(this);
203 if (this.option("expression")) {
204 node.process_expression(true);
206 var passes = +this.options.passes || 1;
207 var min_count = 1 / 0;
208 var stopping = false;
209 var mangle = { ie: this.option("ie") };
210 for (var pass = 0; pass < passes; pass++) {
211 node.figure_out_scope(mangle);
212 if (pass > 0 || this.option("reduce_vars"))
213 node.reset_opt_flags(this);
214 node = node.transform(this);
217 node.walk(new TreeWalker(function() {
220 AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
222 min_count: min_count,
225 if (count < min_count) {
228 } else if (stopping) {
235 if (this.option("expression")) {
236 node.process_expression(false);
240 before: function(node, descend, in_list) {
241 if (node._squeezed) return node;
242 var is_scope = node instanceof AST_Scope;
244 node.hoist_properties(this);
245 node.hoist_declarations(this);
246 node.process_boolean_returns(this);
248 // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
249 // would call AST_Node.transform() if a different instance of AST_Node is
250 // produced after OPT().
251 // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
252 // Migrate and defer all children's AST_Node.transform() to below, which
253 // will now happen after this parent AST_Node has been properly substituted
254 // thus gives a consistent AST snapshot.
256 // Existing code relies on how AST_Node.optimize() worked, and omitting the
257 // following replacement call would result in degraded efficiency of both
258 // output and performance.
260 var opt = node.optimize(this);
261 if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
262 opt.merge_variables(this);
263 opt.drop_unused(this);
266 if (opt === node) opt._squeezed = true;
272 OPT(AST_Node, function(self, compressor) {
276 AST_Node.DEFMETHOD("equivalent_to", function(node) {
277 return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
280 AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) {
281 if (!compressor.option("hoist_exports")) return;
282 var body = this.body, props = [];
283 for (var i = 0; i < body.length; i++) {
285 if (stat instanceof AST_ExportDeclaration) {
286 body[i] = stat = stat.body;
287 if (stat instanceof AST_Definitions) {
288 stat.definitions.forEach(function(defn) {
289 defn.name.match_symbol(export_symbol, true);
292 export_symbol(stat.name);
294 } else if (stat instanceof AST_ExportReferences) {
296 [].push.apply(props, stat.properties);
299 if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props }));
301 function export_symbol(sym) {
302 if (!(sym instanceof AST_SymbolDeclaration)) return;
303 var node = make_node(AST_SymbolExport, sym, sym);
304 node.alias = node.name;
309 AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
311 var tt = new TreeTransformer(function(node) {
312 if (insert && node instanceof AST_SimpleStatement) {
313 return transform ? transform(node) : make_node(AST_Return, node, { value: node.body });
315 if (!insert && node instanceof AST_Return) {
316 return transform ? transform(node) : make_node(AST_SimpleStatement, node, {
317 body: node.value || make_node(AST_UnaryPrefix, node, {
319 expression: make_node(AST_Number, node, { value: 0 }),
323 if (node instanceof AST_Block) {
324 if (node instanceof AST_Lambda) {
325 if (node !== self) return node;
326 } else if (insert === "awaits" && node instanceof AST_Try) {
327 if (node.bfinally) return node;
329 for (var index = node.body.length; --index >= 0;) {
330 var stat = node.body[index];
331 if (!is_declaration(stat, true)) {
332 node.body[index] = stat.transform(tt);
336 } else if (node instanceof AST_If) {
337 node.body = node.body.transform(tt);
338 if (node.alternative) {
339 node.alternative = node.alternative.transform(tt);
341 } else if (node instanceof AST_With) {
342 node.body = node.body.transform(tt);
349 function read_property(obj, node) {
350 var key = node.get_property();
351 if (key instanceof AST_Node) return;
353 if (obj instanceof AST_Array) {
354 var elements = obj.elements;
355 if (key == "length") return make_node_from_constant(elements.length, obj);
356 if (typeof key == "number" && key in elements) value = elements[key];
357 } else if (obj instanceof AST_Lambda) {
358 if (key == "length") {
359 obj.length_read = true;
360 return make_node_from_constant(obj.argnames.length, obj);
362 } else if (obj instanceof AST_Object) {
364 var props = obj.properties;
365 for (var i = props.length; --i >= 0;) {
367 if (!can_hoist_property(prop)) return;
368 if (!value && props[i].key === key) value = props[i].value;
371 return value instanceof AST_SymbolRef && value.fixed_value() || value;
374 function is_read_only_fn(value, name) {
375 if (value instanceof AST_Boolean) return native_fns.Boolean[name];
376 if (value instanceof AST_Number) return native_fns.Number[name];
377 if (value instanceof AST_String) return native_fns.String[name];
378 if (name == "valueOf") return false;
379 if (value instanceof AST_Array) return native_fns.Array[name];
380 if (value instanceof AST_Lambda) return native_fns.Function[name];
381 if (value instanceof AST_Object) return native_fns.Object[name];
382 if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
385 function is_modified(compressor, tw, node, value, level, immutable, recursive) {
386 var parent = tw.parent(level);
387 if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
390 var lhs = is_lhs(node, parent);
392 if (level == 0 && value && value.is_constant()) return;
393 if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
394 if (parent instanceof AST_Assign) switch (parent.operator) {
396 return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
400 return is_modified(compressor, tw, parent, parent, level + 1);
404 if (parent instanceof AST_Binary) {
405 if (!lazy_op[parent.operator]) return;
406 return is_modified(compressor, tw, parent, parent, level + 1);
408 if (parent instanceof AST_Call) {
410 && parent.expression === node
411 && !parent.is_expr_pure(compressor)
412 && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
414 if (parent instanceof AST_Conditional) {
415 if (parent.condition === node) return;
416 return is_modified(compressor, tw, parent, parent, level + 1);
418 if (parent instanceof AST_ForEnumeration) return parent.init === node;
419 if (parent instanceof AST_ObjectKeyVal) {
420 if (parent.value !== node) return;
421 var obj = tw.parent(level + 1);
422 return is_modified(compressor, tw, obj, obj, level + 2);
424 if (parent instanceof AST_PropAccess) {
425 if (parent.expression !== node) return;
426 var prop = read_property(value, parent);
427 return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
429 if (parent instanceof AST_Sequence) {
430 if (parent.tail_node() !== node) return;
431 return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
435 function is_lambda(node) {
436 return node instanceof AST_Class || node instanceof AST_Lambda;
439 function safe_for_extends(node) {
440 return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function;
443 function is_arguments(def) {
444 return def.name == "arguments" && def.scope.uses_arguments;
447 function is_funarg(def) {
448 return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg;
451 function cross_scope(def, sym) {
453 if (def === sym) return false;
454 if (sym instanceof AST_Scope) return true;
455 } while (sym = sym.parent_scope);
458 function can_drop_symbol(ref, compressor, keep_lambda) {
459 var def = ref.definition();
460 if (ref.in_arg && is_funarg(def)) return false;
461 return all(def.orig, function(sym) {
462 if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) {
463 return compressor && can_varify(compressor, sym);
465 return !(keep_lambda && sym instanceof AST_SymbolLambda);
469 function has_escaped(d, scope, node, parent) {
470 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
471 if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
472 if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve();
473 if (parent instanceof AST_VarDef) return parent.value === node;
476 var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
480 function reset_def(tw, compressor, def) {
483 def.cross_loop = false;
484 def.direct_access = false;
486 def.fixed = !def.const_redefs
487 && !def.scope.pinned()
488 && !compressor.exposed(def)
489 && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope)
492 def.recursive_refs = 0;
494 def.should_replace = undefined;
495 def.single_use = undefined;
498 function reset_block_variables(tw, compressor, scope) {
499 scope.variables.each(function(def) {
500 reset_def(tw, compressor, def);
504 function reset_variables(tw, compressor, scope) {
506 scope.variables.each(function(def) {
507 reset_def(tw, compressor, def);
509 if (init instanceof AST_LambdaDefinition) {
510 scope.fn_defs.push(init);
511 init.safe_ids = null;
513 if (def.fixed === null) {
514 def.safe_ids = tw.safe_ids;
516 } else if (def.fixed) {
517 tw.loop_ids[def.id] = tw.in_loop;
521 scope.may_call_this = function() {
522 scope.may_call_this = scope.contains_this() ? return_true : return_false;
524 if (scope.uses_arguments) scope.each_argname(function(node) {
525 node.definition().last_ref = false;
527 if (compressor.option("ie")) scope.variables.each(function(def) {
528 var d = def.orig[0].definition();
529 if (d !== def) d.fixed = false;
533 function walk_fn_def(tw, fn) {
534 var was_scanning = tw.fn_scanning;
537 tw.fn_scanning = was_scanning;
540 function revisit_fn_def(tw, fn) {
541 fn.enclosed.forEach(function(d) {
542 if (fn.variables.get(d.name) === d) return;
543 if (safe_to_read(tw, d)) return;
544 d.single_use = false;
546 if (typeof fixed == "function") fixed = fixed();
547 if (fixed instanceof AST_Lambda && HOP(fixed, "safe_ids")) return;
552 function mark_fn_def(tw, def, fn) {
553 if (!HOP(fn, "safe_ids")) return;
554 var marker = fn.safe_ids;
555 if (marker === false) return;
556 if (fn.parent_scope.resolve().may_call_this === return_true) {
557 if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn);
559 var visited = member(fn, tw.fn_visited);
560 if (marker === tw.safe_ids) {
561 if (!visited) walk_fn_def(tw, fn);
562 } else if (visited) {
563 revisit_fn_def(tw, fn);
567 } else if (tw.fn_scanning && tw.fn_scanning !== def.scope.resolve()) {
570 fn.safe_ids = tw.safe_ids;
575 function pop_scope(tw, scope) {
576 var fn_defs = scope.fn_defs;
577 var tangled = scope.may_call_this === return_true ? fn_defs : fn_defs.filter(function(fn) {
578 if (fn.safe_ids === false) return true;
579 fn.safe_ids = tw.safe_ids;
584 tangled.forEach(function(fn) {
585 fn.safe_ids = tw.safe_ids;
588 fn_defs.forEach(function(fn) {
591 delete scope.fn_defs;
592 delete scope.may_call_this;
596 tw.safe_ids = Object.create(tw.safe_ids);
600 tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
603 function mark(tw, def) {
604 tw.safe_ids[def.id] = {};
607 function push_ref(def, ref) {
608 def.references.push(ref);
609 if (def.last_ref !== false) def.last_ref = ref;
612 function safe_to_read(tw, def) {
613 if (def.single_use == "m") return false;
614 var safe = tw.safe_ids[def.id];
616 var in_order = HOP(tw.safe_ids, def.id);
617 if (!in_order) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids;
618 if (def.fixed == null) {
619 if (is_arguments(def)) return false;
620 if (def.global && def.name == "arguments") return false;
621 tw.loop_ids[def.id] = null;
622 def.fixed = make_node(AST_Undefined, def.orig[0]);
623 if (in_order) delete def.safe_ids;
626 return !safe.assign || safe.assign === tw.safe_ids;
628 return def.fixed instanceof AST_LambdaDefinition;
631 function safe_to_assign(tw, def, declare) {
633 if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false;
634 if (!all(def.orig, function(sym) {
635 return !(sym instanceof AST_SymbolConst);
638 if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
639 return !(sym instanceof AST_SymbolLet);
641 if (def.fixed === false || def.fixed === 0) return false;
642 var safe = tw.safe_ids[def.id];
644 def.safe_ids[def.id] = false;
646 return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read;
648 if (!HOP(tw.safe_ids, def.id)) {
649 if (!safe) return false;
651 var scope = tw.find_parent(AST_BlockScope);
652 if (scope instanceof AST_Class) return false;
653 if (def.scope.resolve() !== scope.resolve()) return false;
655 safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
657 if (def.fixed != null && safe.read) {
658 if (safe.read !== tw.safe_ids) return false;
659 if (tw.loop_ids[def.id] !== tw.in_loop) return false;
661 return safe_to_read(tw, def) && all(def.orig, function(sym) {
662 return !(sym instanceof AST_SymbolLambda);
666 function make_ref(ref, fixed) {
667 var node = make_node(AST_SymbolRef, ref, ref);
668 node.fixed = fixed || make_node(AST_Undefined, ref);
672 function ref_once(compressor, def) {
673 return compressor.option("unused")
674 && !def.scope.pinned()
675 && def.single_use !== false
676 && def.references.length - def.recursive_refs == 1
677 && !(is_funarg(def) && def.scope.uses_arguments);
680 function is_immutable(value) {
681 if (!value) return false;
682 if (value instanceof AST_Assign) {
683 var op = value.operator;
684 return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)];
686 if (value instanceof AST_Sequence) return is_immutable(value.tail_node());
687 return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
690 function value_in_use(node, parent) {
691 if (parent instanceof AST_Array) return true;
692 if (parent instanceof AST_Binary) return lazy_op[parent.operator];
693 if (parent instanceof AST_Conditional) return parent.condition !== node;
694 if (parent instanceof AST_Sequence) return parent.tail_node() === node;
695 if (parent instanceof AST_Spread) return true;
698 function mark_escaped(tw, d, scope, node, value, level, depth) {
699 var parent = tw.parent(level);
700 if (value && value.is_constant()) return;
701 if (has_escaped(d, scope, node, parent)) {
702 d.escaped.push(parent);
703 if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
704 if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
705 if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true;
706 if (d.fixed) d.fixed.escaped = d.escaped;
708 } else if (value_in_use(node, parent)) {
709 mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
710 } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
711 var obj = tw.parent(level + 1);
712 mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
713 } else if (parent instanceof AST_PropAccess && parent.expression === node) {
714 value = read_property(value, parent);
715 mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
718 if (level > 0) return;
719 if (parent instanceof AST_Call && parent.expression === node) return;
720 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
721 if (parent instanceof AST_SimpleStatement) return;
722 if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
723 d.direct_access = true;
724 if (d.fixed) d.fixed.direct_access = true;
727 function mark_assignment_to_arguments(node) {
728 if (!(node instanceof AST_Sub)) return;
729 var expr = node.expression;
730 if (!(expr instanceof AST_SymbolRef)) return;
731 var def = expr.definition();
732 if (!is_arguments(def)) return;
733 var key = node.property;
734 if (key.is_constant()) key = key.value;
735 if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return;
737 (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
738 if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
742 function scan_declaration(tw, compressor, lhs, fixed, visit) {
743 var scanner = new TreeWalker(function(node) {
744 if (node instanceof AST_DefaultValue) {
750 if (save) fixed = function() {
753 if (is_undefined(value, compressor)
754 || (ev = fuzzy_eval(compressor, value, true)) === undefined) {
755 return make_sequence(node, [ value, node.value ]);
757 return ev instanceof AST_Node ? node : value;
759 node.name.walk(scanner);
763 if (node instanceof AST_DestructuredArray) {
766 node.elements.forEach(function(node, index) {
767 if (node instanceof AST_Hole) return reset_flags(node);
768 if (save) fixed = function() {
769 return make_node(AST_Sub, node, {
771 property: make_node(AST_Number, node, { value: index }),
778 if (save) fixed = compressor.option("rests") && function() {
780 if (!(value instanceof AST_Array)) return node;
781 if (!fixed_node) fixed_node = make_node(AST_Array, node);
782 fixed_node.elements = value.elements.slice(node.elements.length);
785 node.rest.walk(scanner);
790 if (node instanceof AST_DestructuredObject) {
793 node.properties.forEach(function(node) {
795 if (node.key instanceof AST_Node) {
800 if (save) fixed = function() {
803 if (typeof key == "string") {
804 if (is_identifier_string(key)) {
807 key = make_node_from_constant(key, node);
810 return make_node(type, node, {
815 node.value.walk(scanner);
819 node.rest.walk(scanner);
824 visit(node, fixed, function() {
825 var save_len = tw.stack.length;
826 for (var i = 0, len = scanner.stack.length - 1; i < len; i++) {
827 tw.stack.push(scanner.stack[i]);
830 tw.stack.length = save_len;
837 function reduce_iife(tw, descend, compressor) {
840 var iife = tw.parent();
841 var hit = is_async(fn) || is_generator(fn);
843 fn.walk(new TreeWalker(function(node) {
844 if (hit) return aborts = true;
845 if (node instanceof AST_Return) return hit = true;
846 if (node instanceof AST_Scope && node !== fn) return true;
848 if (aborts) push(tw);
849 reset_variables(tw, compressor, fn);
850 // Virtually turn IIFE parameters into variable definitions:
851 // (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})()
852 // So existing transformation rules can work on them.
853 var safe = !fn.uses_arguments || tw.has_directive("use strict");
854 fn.argnames.forEach(function(argname, i) {
855 var value = iife.args[i];
856 scan_declaration(tw, compressor, argname, function() {
857 var j = fn.argnames.indexOf(argname);
858 var arg = j < 0 ? value : iife.args[j];
859 if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0];
860 return arg || make_node(AST_Undefined, iife);
863 var rest = fn.rest, fixed_node;
864 if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() {
865 if (fn.rest !== rest) return rest;
866 if (!fixed_node) fixed_node = make_node(AST_Array, fn);
867 fixed_node.elements = iife.args.slice(fn.argnames.length);
871 var safe_ids = tw.safe_ids;
873 if (!aborts) tw.safe_ids = safe_ids;
876 function visit(node, fixed) {
877 var d = node.definition();
878 if (fixed && safe && d.fixed === undefined) {
880 tw.loop_ids[d.id] = tw.in_loop;
882 d.fixed.assigns = [ node ];
889 def(AST_Assign, function(tw, descend, compressor) {
891 var left = node.left;
892 var right = node.right;
893 var ld = left instanceof AST_SymbolRef && left.definition();
894 var scan = ld || left instanceof AST_Destructured;
895 switch (node.operator) {
897 if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
903 if (ld && right instanceof AST_LambdaExpression) {
905 right.parent_scope.resolve().fn_defs.push(right);
906 right.safe_ids = null;
907 if (!ld.fixed || !node.write_only) mark_fn_def(tw, ld, right);
915 mark_assignment_to_arguments(left);
923 mark_assignment_to_arguments(left);
927 var fixed = ld.fixed;
928 if (is_modified(compressor, tw, node, node, 0)) {
932 var safe = safe_to_read(tw, ld);
936 if (safe && !left.in_arg && safe_to_assign(tw, ld)) {
939 if (ld.single_use) ld.single_use = false;
940 left.fixed = ld.fixed = function() {
941 return make_node(AST_Binary, node, {
942 operator: node.operator.slice(0, -1),
943 left: make_ref(left, fixed),
947 left.fixed.assigns = !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
948 left.fixed.assigns.push(node);
956 function walk_prop(lhs) {
957 if (lhs instanceof AST_Dot) {
958 walk_prop(lhs.expression);
959 } else if (lhs instanceof AST_Sub) {
960 walk_prop(lhs.expression);
961 lhs.property.walk(tw);
962 } else if (lhs instanceof AST_SymbolRef) {
963 var d = lhs.definition();
967 if (lhs.fixed.assigns) {
968 lhs.fixed.assigns.push(node);
970 lhs.fixed.assigns = [ node ];
978 function walk_assign() {
979 var recursive = ld && recursive_ref(tw, ld);
980 var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive);
981 scan_declaration(tw, compressor, left, function() {
983 }, function(sym, fixed, walk) {
984 if (!(sym instanceof AST_SymbolRef)) {
985 mark_assignment_to_arguments(sym);
989 var d = sym.definition();
991 if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) {
994 } else if (modified) {
1000 if (left instanceof AST_Destructured
1001 || d.orig.length == 1 && d.orig[0] instanceof AST_SymbolDefun) {
1002 d.single_use = false;
1004 tw.loop_ids[d.id] = tw.in_loop;
1005 sym.fixed = d.fixed = fixed;
1006 sym.fixed.assigns = [ node ];
1007 mark_escaped(tw, d, sym.scope, node, right, 0, 1);
1012 function walk_lazy() {
1021 def(AST_Binary, function(tw) {
1022 if (!lazy_op[this.operator]) return;
1025 this.right.walk(tw);
1029 def(AST_BlockScope, function(tw, descend, compressor) {
1030 reset_block_variables(tw, compressor, this);
1032 def(AST_Call, function(tw, descend) {
1034 var exp = node.expression;
1035 if (exp instanceof AST_LambdaExpression) {
1036 var iife = is_iife_single(node);
1037 node.args.forEach(function(arg) {
1039 if (arg instanceof AST_Spread) iife = false;
1041 if (iife) exp.reduce_vars = reduce_iife;
1043 if (iife) delete exp.reduce_vars;
1046 if (node.TYPE == "Call" && tw.in_boolean_context()) {
1047 if (exp instanceof AST_SymbolRef) {
1048 exp.definition().bool_fn++;
1049 } else if (exp instanceof AST_Assign && exp.operator == "=" && exp.left instanceof AST_SymbolRef) {
1050 exp.left.definition().bool_fn++;
1054 var optional = node.optional;
1055 if (optional) push(tw);
1056 node.args.forEach(function(arg) {
1059 if (optional) pop(tw);
1060 var fixed = exp instanceof AST_SymbolRef && exp.fixed_value();
1061 if (fixed instanceof AST_Lambda) {
1062 mark_fn_def(tw, exp.definition(), fixed);
1064 tw.find_parent(AST_Scope).may_call_this();
1068 def(AST_Class, function(tw, descend, compressor) {
1070 reset_block_variables(tw, compressor, node);
1071 if (node.extends) node.extends.walk(tw);
1072 var props = node.properties.filter(function(prop) {
1074 if (prop.key instanceof AST_Node) prop.key.walk(tw);
1078 var d = node.name.definition();
1079 var parent = tw.parent();
1080 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false;
1081 if (safe_to_assign(tw, d, true)) {
1083 tw.loop_ids[d.id] = tw.in_loop;
1084 d.fixed = function() {
1087 d.fixed.assigns = [ node ];
1088 if (!is_safe_lexical(d)) d.single_use = false;
1093 props.forEach(function(prop) {
1094 if (!prop.static || prop instanceof AST_ClassField && prop.value.contains_this()) {
1096 prop.value.walk(tw);
1099 prop.value.walk(tw);
1104 def(AST_Conditional, function(tw) {
1105 this.condition.walk(tw);
1107 this.consequent.walk(tw);
1110 this.alternative.walk(tw);
1114 def(AST_DefaultValue, function(tw) {
1117 this.value.walk(tw);
1121 def(AST_Do, function(tw) {
1122 var save_loop = tw.in_loop;
1126 if (has_loop_control(this, tw.parent())) {
1130 this.condition.walk(tw);
1132 tw.in_loop = save_loop;
1135 def(AST_For, function(tw, descend, compressor) {
1137 reset_block_variables(tw, compressor, node);
1138 if (node.init) node.init.walk(tw);
1139 var save_loop = tw.in_loop;
1142 if (node.condition) node.condition.walk(tw);
1145 if (has_loop_control(node, tw.parent())) {
1152 tw.in_loop = save_loop;
1155 def(AST_ForEnumeration, function(tw, descend, compressor) {
1157 reset_block_variables(tw, compressor, node);
1158 node.object.walk(tw);
1159 var save_loop = tw.in_loop;
1162 var init = node.init;
1163 if (init instanceof AST_Definitions) {
1164 init.definitions[0].name.mark_symbol(function(node) {
1165 if (node instanceof AST_SymbolDeclaration) {
1166 var def = node.definition();
1171 } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) {
1172 init.mark_symbol(function(node) {
1173 if (node instanceof AST_SymbolRef) {
1174 var def = node.definition();
1175 push_ref(def, node);
1177 if (!node.is_immutable()) def.fixed = false;
1185 tw.in_loop = save_loop;
1188 def(AST_If, function(tw) {
1189 this.condition.walk(tw);
1193 if (this.alternative) {
1195 this.alternative.walk(tw);
1200 def(AST_LabeledStatement, function(tw) {
1206 def(AST_Lambda, function(tw, descend, compressor) {
1208 if (HOP(fn, "safe_ids") && fn.safe_ids !== tw.safe_ids) return true;
1209 if (!push_uniq(tw.fn_visited, fn)) return true;
1212 reset_variables(tw, compressor, fn);
1215 if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
1218 def(AST_LambdaDefinition, function(tw, descend, compressor) {
1220 var def = fn.name.definition();
1221 var parent = tw.parent();
1222 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false;
1223 if (HOP(fn, "safe_ids") && fn.safe_ids !== tw.safe_ids) return true;
1224 if (!push_uniq(tw.fn_visited, fn)) return true;
1227 reset_variables(tw, compressor, fn);
1232 def(AST_Sub, function(tw) {
1233 if (!this.optional) return;
1234 this.expression.walk(tw);
1236 this.property.walk(tw);
1240 def(AST_Switch, function(tw, descend, compressor) {
1242 reset_block_variables(tw, compressor, node);
1243 node.expression.walk(tw);
1245 node.body.forEach(function(branch) {
1246 if (branch instanceof AST_Default) return;
1247 branch.expression.walk(tw);
1253 if (!first) pop(tw);
1254 walk_body(node, tw);
1257 def(AST_SwitchBranch, function(tw) {
1259 walk_body(this, tw);
1263 def(AST_SymbolCatch, function() {
1264 this.definition().fixed = false;
1266 def(AST_SymbolImport, function() {
1267 this.definition().fixed = false;
1269 def(AST_SymbolRef, function(tw, descend, compressor) {
1270 var d = this.definition();
1272 if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) {
1273 tw.loop_ids[d.id] = tw.in_loop;
1275 var recursive = recursive_ref(tw, d);
1276 if (recursive) recursive.enclosed.forEach(function(def) {
1277 if (d === def) return;
1278 if (def.scope.resolve() === recursive) return;
1279 var assigns = def.fixed && def.fixed.assigns;
1280 if (!assigns) return;
1281 if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
1282 var safe = tw.safe_ids[def.id];
1286 if (d.fixed === false || d.fixed === 0) {
1287 var redef = d.redefined();
1288 if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false;
1289 } else if (d.fixed === undefined || !safe_to_read(tw, d)) {
1291 } else if (d.fixed) {
1292 if (this.in_arg && d.orig[0] instanceof AST_SymbolLambda) this.fixed = d.scope;
1293 var value = this.fixed_value();
1296 } else if (value && ref_once(compressor, d)) {
1297 d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
1298 d.single_use = is_lambda(value)
1300 && (!d.in_loop || tw.parent() instanceof AST_Call)
1302 && d.scope === this.scope.resolve()
1303 && value.is_constant_expression();
1305 d.single_use = false;
1307 if (is_modified(compressor, tw, this, value, 0, is_immutable(value), recursive)) {
1314 if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true;
1315 mark_escaped(tw, d, this.scope, this, value, 0, 1);
1317 if (!this.fixed) this.fixed = d.fixed;
1319 if (value instanceof AST_Lambda
1320 && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
1321 mark_fn_def(tw, d, value);
1324 def(AST_Template, function(tw, descend) {
1328 if (tag instanceof AST_LambdaExpression) {
1329 node.expressions.forEach(function(exp) {
1336 node.expressions.forEach(function(exp) {
1339 var fixed = tag instanceof AST_SymbolRef && tag.fixed_value();
1340 if (fixed instanceof AST_Lambda) {
1341 mark_fn_def(tw, tag.definition(), fixed);
1343 tw.find_parent(AST_Scope).may_call_this();
1347 def(AST_Toplevel, function(tw, descend, compressor) {
1349 node.globals.each(function(def) {
1350 reset_def(tw, compressor, def);
1353 reset_variables(tw, compressor, node);
1355 pop_scope(tw, node);
1358 def(AST_Try, function(tw, descend, compressor) {
1360 reset_block_variables(tw, compressor, node);
1362 walk_body(node, tw);
1366 node.bcatch.walk(tw);
1369 if (node.bfinally) node.bfinally.walk(tw);
1372 def(AST_Unary, function(tw, descend) {
1374 if (!UNARY_POSTFIX[node.operator]) return;
1375 var exp = node.expression;
1376 if (!(exp instanceof AST_SymbolRef)) {
1377 mark_assignment_to_arguments(exp);
1380 var d = exp.definition();
1382 var fixed = d.fixed;
1383 if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) {
1386 if (d.single_use) d.single_use = false;
1387 d.fixed = function() {
1388 return make_node(AST_Binary, node, {
1389 operator: node.operator.slice(0, -1),
1390 left: make_node(AST_UnaryPrefix, node, {
1392 expression: make_ref(exp, fixed)
1394 right: make_node(AST_Number, node, {
1399 d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
1400 d.fixed.assigns.push(node);
1401 if (node instanceof AST_UnaryPrefix) {
1402 exp.fixed = d.fixed;
1404 exp.fixed = function() {
1405 return make_node(AST_UnaryPrefix, node, {
1407 expression: make_ref(exp, fixed)
1410 exp.fixed.assigns = fixed && fixed.assigns;
1418 def(AST_VarDef, function(tw, descend, compressor) {
1420 var value = node.value;
1421 if (value instanceof AST_LambdaExpression && node.name instanceof AST_SymbolDeclaration) {
1423 value.parent_scope.resolve().fn_defs.push(value);
1424 value.safe_ids = null;
1425 var ld = node.name.definition();
1426 if (!ld.fixed) mark_fn_def(tw, ld, value);
1430 } else if (tw.parent() instanceof AST_Let) {
1435 function walk_defn() {
1436 scan_declaration(tw, compressor, node.name, function() {
1437 return node.value || make_node(AST_Undefined, node);
1438 }, function(name, fixed) {
1439 var d = name.definition();
1440 if (fixed && safe_to_assign(tw, d, true)) {
1442 tw.loop_ids[d.id] = tw.in_loop;
1444 d.fixed.assigns = [ node ];
1445 if (name instanceof AST_SymbolConst && d.redefined()
1446 || !(can_drop_symbol(name) || is_safe_lexical(d))) {
1447 d.single_use = false;
1455 def(AST_While, function(tw, descend) {
1456 var save_loop = tw.in_loop;
1461 tw.in_loop = save_loop;
1464 })(function(node, func) {
1465 node.DEFMETHOD("reduce_vars", func);
1468 function reset_flags(node) {
1469 node._squeezed = false;
1470 node._optimized = false;
1472 if (node instanceof AST_Scope) delete node._var_names;
1475 AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
1476 var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
1478 return node.reduce_vars(tw, descend, compressor);
1480 // Flow control for visiting lambda definitions
1481 tw.fn_scanning = null;
1483 // Record the loop body in which `AST_SymbolDeclaration` is first encountered
1485 tw.loop_ids = Object.create(null);
1486 // Stack of look-up tables to keep track of whether a `SymbolDef` has been
1487 // properly assigned before use:
1488 // - `push()` & `pop()` when visiting conditional branches
1489 // - backup & restore via `save_ids` when visiting out-of-order sections
1490 tw.safe_ids = Object.create(null);
1494 AST_Symbol.DEFMETHOD("fixed_value", function() {
1495 var fixed = this.definition().fixed;
1497 if (this.fixed) fixed = this.fixed;
1498 return fixed instanceof AST_Node ? fixed : fixed();
1500 fixed = fixed === 0 && this.fixed;
1501 if (!fixed) return fixed;
1502 var value = fixed instanceof AST_Node ? fixed : fixed();
1503 return value.is_constant() && value;
1506 AST_SymbolRef.DEFMETHOD("is_immutable", function() {
1507 var def = this.redef || this.definition();
1508 return (this.in_arg || def.orig.length == 1) && def.orig[0] instanceof AST_SymbolLambda;
1511 AST_Node.DEFMETHOD("convert_symbol", noop);
1512 function convert_destructured(type, process) {
1513 return this.transform(new TreeTransformer(function(node, descend) {
1514 if (node instanceof AST_DefaultValue) {
1515 node = node.clone();
1516 node.name = node.name.transform(this);
1519 if (node instanceof AST_Destructured) {
1520 node = node.clone();
1521 descend(node, this);
1524 if (node instanceof AST_DestructuredKeyVal) {
1525 node = node.clone();
1526 node.value = node.value.transform(this);
1529 return node.convert_symbol(type, process);
1532 AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured);
1533 AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
1534 function convert_symbol(type, process) {
1535 var node = make_node(type, this, this);
1536 process(node, this);
1539 AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
1540 AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
1542 function mark_destructured(process, tw) {
1543 var marker = new TreeWalker(function(node) {
1544 if (node instanceof AST_DefaultValue) {
1545 node.value.walk(tw);
1546 node.name.walk(marker);
1549 if (node instanceof AST_DestructuredKeyVal) {
1550 if (node.key instanceof AST_Node) node.key.walk(tw);
1551 node.value.walk(marker);
1554 return process(node);
1558 AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured);
1559 AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured);
1560 function mark_symbol(process) {
1561 return process(this);
1563 AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol);
1564 AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol);
1566 AST_Node.DEFMETHOD("match_symbol", function(predicate) {
1567 return predicate(this);
1569 function match_destructured(predicate, ignore_side_effects) {
1571 var tw = new TreeWalker(function(node) {
1572 if (found) return true;
1573 if (node instanceof AST_DefaultValue) {
1574 if (!ignore_side_effects) return found = true;
1578 if (node instanceof AST_DestructuredKeyVal) {
1579 if (!ignore_side_effects && node.key instanceof AST_Node) return found = true;
1580 node.value.walk(tw);
1583 if (predicate(node)) return found = true;
1588 AST_DefaultValue.DEFMETHOD("match_symbol", match_destructured);
1589 AST_Destructured.DEFMETHOD("match_symbol", match_destructured);
1591 function in_async_generator(scope) {
1592 return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction;
1595 function find_scope(compressor) {
1596 var level = 0, node;
1597 while (node = compressor.parent(level++)) {
1598 if (node.variables) return node;
1602 var identifier_atom = makePredicate("Infinity NaN undefined");
1603 function is_lhs_read_only(lhs, compressor) {
1604 if (lhs instanceof AST_ObjectIdentity) return true;
1605 if (lhs instanceof AST_PropAccess) {
1606 if (lhs.property === "__proto__") return true;
1607 lhs = lhs.expression;
1608 if (lhs instanceof AST_SymbolRef) {
1609 if (lhs.is_immutable()) return false;
1610 lhs = lhs.fixed_value();
1612 if (!lhs) return true;
1613 if (lhs.tail_node().is_constant()) return true;
1614 return is_lhs_read_only(lhs, compressor);
1616 if (lhs instanceof AST_SymbolRef) {
1617 if (lhs.is_immutable()) return true;
1618 var def = lhs.definition();
1619 return compressor.exposed(def) && identifier_atom[def.name];
1624 function make_node(ctor, orig, props) {
1625 if (!props) props = {};
1627 if (!props.start) props.start = orig.start;
1628 if (!props.end) props.end = orig.end;
1630 return new ctor(props);
1633 function make_sequence(orig, expressions) {
1634 if (expressions.length == 1) return expressions[0];
1635 return make_node(AST_Sequence, orig, {
1636 expressions: expressions.reduce(merge_sequence, [])
1640 function make_node_from_constant(val, orig) {
1641 switch (typeof val) {
1643 return make_node(AST_String, orig, {
1647 if (isNaN(val)) return make_node(AST_NaN, orig);
1648 if (isFinite(val)) {
1649 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
1651 expression: make_node(AST_Number, orig, { value: -val })
1652 }) : make_node(AST_Number, orig, { value: val });
1654 return val < 0 ? make_node(AST_UnaryPrefix, orig, {
1656 expression: make_node(AST_Infinity, orig)
1657 }) : make_node(AST_Infinity, orig);
1659 return make_node(val ? AST_True : AST_False, orig);
1661 return make_node(AST_Undefined, orig);
1664 return make_node(AST_Null, orig, { value: null });
1666 if (val instanceof RegExp) {
1667 return make_node(AST_RegExp, orig, { value: val });
1669 throw new Error(string_template("Can't handle constant of type: {type}", {
1675 function needs_unbinding(compressor, val) {
1676 return val instanceof AST_PropAccess
1677 || is_undeclared_ref(val) && val.name == "eval";
1680 // we shouldn't compress (1,func)(something) to
1681 // func(something) because that changes the meaning of
1682 // the func (becomes lexical instead of global).
1683 function maintain_this_binding(compressor, parent, orig, val) {
1685 if (parent.TYPE == "Call") {
1686 wrap = parent.expression === orig && needs_unbinding(compressor, val);
1687 } else if (parent instanceof AST_Template) {
1688 wrap = parent.tag === orig && needs_unbinding(compressor, val);
1689 } else if (parent instanceof AST_UnaryPrefix) {
1690 wrap = parent.operator == "delete"
1691 || parent.operator == "typeof" && is_undeclared_ref(val);
1693 return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
1696 function merge_sequence(array, node) {
1697 if (node instanceof AST_Sequence) {
1698 array.push.apply(array, node.expressions);
1705 function is_lexical_definition(stat) {
1706 return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let;
1709 function safe_to_trim(stat) {
1710 if (stat instanceof AST_LambdaDefinition) {
1711 var def = stat.name.definition();
1712 var scope = stat.name.scope;
1713 return def.scope === scope || all(def.references, function(ref) {
1716 if (s === scope) return true;
1717 } while (s = s.parent_scope);
1720 return !is_lexical_definition(stat);
1723 function as_statement_array(thing) {
1724 if (thing === null) return [];
1725 if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ];
1726 if (thing instanceof AST_EmptyStatement) return [];
1727 if (is_statement(thing)) return [ thing ];
1728 throw new Error("Can't convert thing to statement array");
1731 function is_empty(thing) {
1732 if (thing === null) return true;
1733 if (thing instanceof AST_EmptyStatement) return true;
1734 if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
1738 function has_declarations_only(block) {
1739 return all(block.body, function(stat) {
1740 return is_empty(stat)
1741 || stat instanceof AST_Defun
1742 || stat instanceof AST_Var && declarations_only(stat);
1746 function loop_body(x) {
1747 if (x instanceof AST_IterationStatement) {
1748 return x.body instanceof AST_BlockStatement ? x.body : x;
1753 function is_iife_call(node) {
1754 if (node.TYPE != "Call") return false;
1756 node = node.expression;
1757 } while (node instanceof AST_PropAccess);
1758 return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
1761 function is_iife_single(call) {
1762 var exp = call.expression;
1763 if (exp.name) return false;
1764 if (!(call instanceof AST_New)) return true;
1766 exp.walk(new TreeWalker(function(node) {
1767 if (found) return true;
1768 if (node instanceof AST_NewTarget) return found = true;
1769 if (node instanceof AST_Scope && node !== exp) return true;
1774 function is_undeclared_ref(node) {
1775 return node instanceof AST_SymbolRef && node.definition().undeclared;
1778 var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Map Math Number parseFloat parseInt RangeError ReferenceError RegExp Object Set setInterval setTimeout String SyntaxError TypeError unescape URIError WeakMap WeakSet");
1779 AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
1781 || !this.definition().undeclared
1782 || compressor.option("unsafe") && global_names[this.name];
1785 function declarations_only(node) {
1786 return all(node.definitions, function(var_def) {
1787 return !var_def.value;
1791 function is_declaration(stat, lexical) {
1792 if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) {
1793 if (prop.key instanceof AST_Node) return false;
1794 if (prop instanceof AST_ClassField && prop.static && prop.value) return false;
1797 if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat);
1798 if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical);
1799 if (stat instanceof AST_ExportDefault) return is_declaration(stat.body, lexical);
1800 return stat instanceof AST_LambdaDefinition;
1803 function is_last_statement(body, stat) {
1804 var index = body.lastIndexOf(stat);
1805 if (index < 0) return false;
1806 while (++index < body.length) {
1807 if (!is_declaration(body[index], true)) return false;
1812 function tighten_body(statements, compressor) {
1813 var in_loop, in_try, scope;
1814 find_loop_scope_try();
1815 var CHANGED, max_iter = 10;
1818 eliminate_spurious_blocks(statements);
1819 if (compressor.option("dead_code")) {
1820 eliminate_dead_code(statements, compressor);
1822 if (compressor.option("if_return")) {
1823 handle_if_return(statements, compressor);
1825 if (compressor.sequences_limit > 0) {
1826 sequencesize(statements, compressor);
1827 sequencesize_2(statements, compressor);
1829 if (compressor.option("join_vars")) {
1830 join_consecutive_vars(statements);
1832 if (compressor.option("collapse_vars")) {
1833 collapse(statements, compressor);
1835 } while (CHANGED && max_iter-- > 0);
1838 function find_loop_scope_try() {
1839 var node = compressor.self(), level = 0;
1841 if (node instanceof AST_Catch) {
1842 if (compressor.parent(level).bfinally) {
1843 if (!in_try) in_try = {};
1844 in_try.bfinally = true;
1847 } else if (node instanceof AST_Finally) {
1849 } else if (node instanceof AST_IterationStatement) {
1851 } else if (node instanceof AST_Scope) {
1854 } else if (node instanceof AST_Try) {
1855 if (!in_try) in_try = {};
1856 if (node.bcatch) in_try.bcatch = true;
1857 if (node.bfinally) in_try.bfinally = true;
1859 } while (node = compressor.parent(level++));
1862 // Search from right to left for assignment-like expressions:
1866 // For each candidate, scan from left to right for first usage, then try
1867 // to fold assignment into the site for compression.
1868 // Will not attempt to collapse assignments into or past code blocks
1869 // which are not sequentially executed, e.g. loops and conditionals.
1870 function collapse(statements, compressor) {
1871 if (scope.pinned()) return statements;
1873 var assignments = Object.create(null);
1874 var candidates = [];
1875 var declare_only = Object.create(null);
1877 var stat_index = statements.length;
1878 var scanner = new TreeTransformer(function(node, descend) {
1879 if (abort) return node;
1880 // Skip nodes before `candidate` as quickly as possible
1882 if (node !== hit_stack[hit_index]) return node;
1884 if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner);
1886 stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
1887 if (stop_after === node) abort = true;
1890 // Stop immediately if these node types are encountered
1891 var parent = scanner.parent();
1892 if (should_stop(node, parent)) {
1896 // Stop only if candidate is found within conditional branches
1897 if (!stop_if_hit && in_conditional(node, parent)) {
1898 stop_if_hit = parent;
1900 // Skip transient nodes caused by single-use variable replacement
1901 if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
1902 // Replace variable with assignment when found
1904 if (!(node instanceof AST_SymbolDeclaration)
1905 && (scan_lhs && lhs.equivalent_to(node)
1906 || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
1907 if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
1908 if (!hit_rhs && !value_def) abort = true;
1911 if (is_lhs(node, parent)) {
1912 if (value_def && !hit_rhs) assign_used = true;
1914 } else if (value_def) {
1915 if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced;
1916 if (!hit_rhs) replaced++;
1921 CHANGED = abort = true;
1922 AST_Node.info("Collapsing {node} [{file}:{line},{col}]", {
1924 file: node.start.file,
1925 line: node.start.line,
1926 col: node.start.col,
1928 if (candidate.TYPE == "Binary") return make_node(AST_Assign, candidate, {
1930 left: candidate.right.left,
1931 right: make_node(AST_Conditional, candidate, {
1932 condition: candidate.operator == "&&" ? candidate.left : candidate.left.negate(compressor),
1933 consequent: candidate.right.right,
1937 if (candidate instanceof AST_UnaryPostfix) {
1938 if (lhs instanceof AST_SymbolRef) lhs.definition().fixed = false;
1939 return make_node(AST_UnaryPrefix, candidate, candidate);
1941 if (candidate instanceof AST_VarDef) {
1942 var def = candidate.name.definition();
1943 if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
1945 return maintain_this_binding(compressor, parent, node, candidate.value);
1947 return make_node(AST_Assign, candidate, {
1949 left: make_node(AST_SymbolRef, candidate.name, candidate.name),
1950 right: candidate.value,
1953 var assign = candidate;
1954 while (assign.write_only) {
1955 assign.write_only = false;
1956 if (!(assign instanceof AST_Assign)) break;
1957 assign = assign.right;
1961 // These node types have child nodes that execute sequentially,
1962 // but are otherwise not safe to scan into or beyond them.
1963 if (is_last_node(node, parent) || may_throw(node)) {
1965 if (node instanceof AST_Scope) abort = true;
1967 // Scan but don't replace inside getter/setter
1968 if (node instanceof AST_Accessor) {
1969 var replace = can_replace;
1970 can_replace = false;
1971 descend(node, scanner);
1972 can_replace = replace;
1973 return signal_abort(node);
1975 // Scan but don't replace inside destructuring expression
1976 if (node instanceof AST_Destructured) {
1977 var replace = can_replace;
1978 can_replace = false;
1979 descend(node, scanner);
1980 can_replace = replace;
1981 return signal_abort(node);
1983 // Scan but don't replace inside default value
1984 if (node instanceof AST_DefaultValue) {
1985 node.name = node.name.transform(scanner);
1986 var replace = can_replace;
1987 can_replace = false;
1988 node.value = node.value.transform(scanner);
1989 can_replace = replace;
1990 return signal_abort(node);
1992 // Scan but don't replace inside block scope with colliding variable
1993 if (node instanceof AST_BlockScope
1994 && !(node instanceof AST_Scope)
1995 && !(node.variables && node.variables.all(function(def) {
1996 return !lvalues.has(def.name);
1998 var replace = can_replace;
1999 can_replace = false;
2000 if (!handle_custom_scan_order(node, scanner)) descend(node, scanner);
2001 can_replace = replace;
2002 return signal_abort(node);
2004 return handle_custom_scan_order(node, scanner);
2006 var multi_replacer = new TreeTransformer(function(node) {
2007 if (abort) return node;
2008 // Skip nodes before `candidate` as quickly as possible
2010 if (node !== hit_stack[hit_index]) return node;
2012 switch (hit_stack.length - hit_index) {
2015 if (assign_used) return node;
2016 if (node !== candidate) return node;
2017 if (node instanceof AST_VarDef) return node;
2019 var parent = multi_replacer.parent();
2020 if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
2021 value_def.replaced++;
2026 if (!assign_used && node.body === candidate) {
2029 value_def.replaced++;
2033 return handle_custom_scan_order(node, multi_replacer);
2036 // Replace variable when found
2037 if (node instanceof AST_SymbolRef && node.definition() === def) {
2038 if (is_lhs(node, multi_replacer.parent())) return node;
2039 if (!--replaced) abort = true;
2040 var ref = rvalue.clone();
2041 ref.scope = node.scope;
2043 if (replaced == assign_pos) {
2045 return make_node(AST_Assign, candidate, {
2054 // Skip (non-executed) functions and (leading) default case in switch statements
2055 if (node instanceof AST_Default || node instanceof AST_Scope) return node;
2057 while (--stat_index >= 0) {
2058 // Treat parameters as collapsible in IIFE, i.e.
2059 // function(a, b){ ... }(x());
2060 // would be translated into equivalent assignments:
2061 // var a = x(), b = undefined;
2062 if (stat_index == 0 && compressor.option("unused")) extract_args();
2063 // Find collapsible assignments
2065 extract_candidates(statements[stat_index]);
2066 while (candidates.length > 0) {
2067 hit_stack = candidates.pop();
2069 var candidate = hit_stack[hit_stack.length - 1];
2070 var assign_pos = -1;
2071 var assign_used = false;
2073 var value_def = null;
2074 var stop_after = null;
2075 var stop_if_hit = null;
2076 var lhs = get_lhs(candidate);
2077 var side_effects = lhs && lhs.has_side_effects(compressor);
2078 var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
2079 var scan_rhs = foldable(candidate);
2080 if (!scan_lhs && !scan_rhs) continue;
2081 var funarg = candidate.name instanceof AST_SymbolFunarg;
2082 var may_throw = return_false;
2083 if (candidate.may_throw(compressor)) {
2084 if (funarg && is_async(scope)) continue;
2085 may_throw = in_try ? function(node) {
2086 return node.has_side_effects(compressor);
2087 } : side_effects_external;
2089 var read_toplevel = false;
2090 var modify_toplevel = false;
2091 // Locate symbols which may execute code outside of scanning range
2092 var well_defined = true;
2093 var lvalues = get_lvalues(candidate);
2094 var lhs_local = is_lhs_local(lhs);
2095 var rvalue = get_rvalue(candidate);
2096 if (!side_effects) side_effects = value_has_side_effects();
2097 var check_destructured = in_try || !lhs_local ? function(node) {
2098 return node instanceof AST_Destructured;
2100 var replace_all = replace_all_symbols(candidate);
2104 var can_replace = !args || !hit;
2106 for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) {
2107 if (args[j]) args[j].transform(scanner);
2111 for (var i = stat_index; !abort && i < statements.length; i++) {
2112 statements[i].transform(scanner);
2115 if (!replaced || remaining > replaced + assign_used) {
2116 candidates.push(hit_stack);
2117 force_single = true;
2120 if (replaced == assign_pos) assign_used = true;
2121 var def = lhs.definition();
2125 for (var i = stat_index; !abort && i < statements.length; i++) {
2126 if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
2128 replaced = candidate instanceof AST_VarDef
2129 && candidate === hit_stack[hit_stack.length - 1]
2130 && def.references.length == def.replaced
2131 && !compressor.exposed(def);
2132 value_def.last_ref = false;
2133 value_def.single_use = false;
2136 if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
2140 function signal_abort(node) {
2141 if (abort) return node;
2142 if (stop_after === node) abort = true;
2143 if (stop_if_hit === node) stop_if_hit = null;
2147 function handle_custom_scan_order(node, tt) {
2148 if (!(node instanceof AST_BlockScope)) {
2149 if (!(node instanceof AST_ClassProperty && !node.static)) return;
2150 // Skip non-static class property values
2151 if (node.key instanceof AST_Node) node.key = node.key.transform(tt);
2154 // Skip (non-executed) functions
2155 if (node instanceof AST_Scope) return node;
2156 // Scan object only in a for-in/of statement
2157 if (node instanceof AST_ForEnumeration) {
2158 node.object = node.object.transform(tt);
2162 // Scan first case expression only in a switch statement
2163 if (node instanceof AST_Switch) {
2164 node.expression = node.expression.transform(tt);
2165 for (var i = 0; !abort && i < node.body.length; i++) {
2166 var branch = node.body[i];
2167 if (branch instanceof AST_Case) {
2169 if (branch !== hit_stack[hit_index]) continue;
2172 branch.expression = branch.expression.transform(tt);
2173 if (!replace_all) break;
2182 function is_direct_assignment(node, parent) {
2183 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node;
2184 if (parent instanceof AST_DefaultValue) return parent.name === node;
2185 if (parent instanceof AST_DestructuredArray) return true;
2186 if (parent instanceof AST_DestructuredKeyVal) return parent.value === node;
2189 function should_stop(node, parent) {
2190 if (node === rvalue) return true;
2191 if (parent instanceof AST_For) {
2192 if (node !== parent.init) return true;
2194 if (node instanceof AST_Assign) {
2195 return node.operator != "=" && lhs.equivalent_to(node.left);
2197 if (node instanceof AST_Call) {
2198 if (!(lhs instanceof AST_PropAccess)) return false;
2199 if (!lhs.equivalent_to(node.expression)) return false;
2200 return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
2202 if (node instanceof AST_Class) return !compressor.has_directive("use strict");
2203 if (node instanceof AST_Debugger) return true;
2204 if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
2205 if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node;
2206 if (node instanceof AST_DWLoop) return true;
2207 if (node instanceof AST_LoopControl) return true;
2208 if (node instanceof AST_SymbolRef) {
2209 if (node.is_declared(compressor)) {
2210 if (node.fixed_value()) return false;
2211 if (can_drop_symbol(node)) {
2212 return !(parent instanceof AST_PropAccess && parent.expression === node)
2213 && is_arguments(node.definition());
2215 } else if (is_direct_assignment(node, parent)) {
2218 if (!replace_all) return true;
2222 if (node instanceof AST_Try) return true;
2223 if (node instanceof AST_With) return true;
2227 function in_conditional(node, parent) {
2228 if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
2229 if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
2230 if (parent instanceof AST_Call) return parent.optional && parent.expression !== node;
2231 if (parent instanceof AST_Case) return parent.expression !== node;
2232 if (parent instanceof AST_Conditional) return parent.condition !== node;
2233 if (parent instanceof AST_If) return parent.condition !== node;
2234 if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node;
2237 function is_last_node(node, parent) {
2238 if (node instanceof AST_Await) return true;
2239 if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right);
2240 if (node instanceof AST_Call) {
2241 var def, fn = node.expression;
2242 if (fn instanceof AST_SymbolRef) {
2243 def = fn.definition();
2244 fn = fn.fixed_value();
2246 if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor);
2247 if (def && recursive_ref(compressor, def, fn)) return true;
2248 if (fn.collapse_scanning) return false;
2249 fn.collapse_scanning = true;
2250 var replace = can_replace;
2251 can_replace = false;
2252 var after = stop_after;
2253 var if_hit = stop_if_hit;
2254 if (!all(fn.argnames, function(argname) {
2255 if (argname instanceof AST_DefaultValue) {
2256 argname.value.transform(scanner);
2257 if (abort) return false;
2258 argname = argname.name;
2260 return !(argname instanceof AST_Destructured);
2263 } else if (is_arrow(fn) && fn.value) {
2264 fn.value.transform(scanner);
2265 } else for (var i = 0; !abort && i < fn.body.length; i++) {
2266 var stat = fn.body[i];
2267 if (stat instanceof AST_Return) {
2268 if (stat.value) stat.value.transform(scanner);
2271 stat.transform(scanner);
2273 stop_if_hit = if_hit;
2275 can_replace = replace;
2276 delete fn.collapse_scanning;
2277 if (!abort) return false;
2281 if (node instanceof AST_Exit) {
2283 if (in_try.bfinally) return true;
2284 if (in_try.bcatch && node instanceof AST_Throw) return true;
2286 return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
2288 if (node instanceof AST_Function) {
2289 return compressor.option("ie") && node.name && lvalues.has(node.name.name);
2291 if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent);
2292 if (node instanceof AST_PropAccess) {
2293 if (side_effects) return true;
2294 var exp = node.expression;
2295 if (exp instanceof AST_SymbolRef && is_arguments(exp.definition())) return true;
2296 if (compressor.option("unsafe")) {
2297 if (is_undeclared_ref(exp) && global_names[exp.name]) return false;
2298 if (is_static_fn(exp)) return false;
2300 if (!well_defined) return true;
2301 if (value_def) return false;
2302 if (!in_try && lhs_local) return false;
2303 if (node.optional) return false;
2304 return exp.may_throw_on_access(compressor);
2306 if (node instanceof AST_Spread) return true;
2307 if (node instanceof AST_SymbolRef) {
2308 if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent);
2309 if (side_effects && may_modify(node)) return true;
2310 var def = node.definition();
2311 return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
2313 if (node instanceof AST_Template) return !node.is_expr_pure(compressor);
2314 if (node instanceof AST_VarDef) {
2315 if (check_destructured(node.name)) return true;
2316 return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
2317 return node instanceof AST_SymbolDeclaration
2318 && (lvalues.has(node.name) || side_effects && may_modify(node));
2321 if (node instanceof AST_Yield) return true;
2322 var sym = is_lhs(node.left, node);
2323 if (!sym) return false;
2324 if (sym instanceof AST_PropAccess) return true;
2325 if (check_destructured(sym)) return true;
2326 return sym.match_symbol(function(node) {
2327 return node instanceof AST_SymbolRef
2328 && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition()));
2332 function may_throw_destructured(node, value) {
2333 if (!value) return !(node instanceof AST_Symbol);
2334 if (node instanceof AST_DefaultValue) {
2335 return value.has_side_effects(compressor)
2336 || node.value.has_side_effects(compressor)
2337 || may_throw_destructured(node.name, is_undefined(value) && node.value);
2339 if (node instanceof AST_Destructured) {
2340 if (node.rest && may_throw_destructured(node.rest)) return true;
2341 if (node instanceof AST_DestructuredArray) {
2342 if (!(value instanceof AST_Array || value.is_string(compressor))) return true;
2343 return !all(node.elements, function(element) {
2344 return !may_throw_destructured(element);
2347 if (node instanceof AST_DestructuredObject) {
2348 if (!value.is_defined(compressor)) return true;
2349 return !all(node.properties, function(prop) {
2350 if (prop instanceof AST_Node && prop.has_side_effects(compressor)) return false;
2351 return !may_throw_destructured(prop.value);
2357 function extract_args() {
2358 var iife, fn = compressor.self();
2359 if (fn instanceof AST_LambdaExpression
2360 && !is_generator(fn)
2361 && !fn.uses_arguments
2363 && (iife = compressor.parent()) instanceof AST_Call
2364 && iife.expression === fn
2365 && is_iife_single(iife)
2366 && all(iife.args, function(arg) {
2367 return !(arg instanceof AST_Spread);
2369 var fn_strict = compressor.has_directive("use strict");
2370 if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
2371 var has_await = is_async(fn) ? function(node) {
2372 return node instanceof AST_Symbol && node.name == "await";
2373 } : function(node) {
2374 return node instanceof AST_Await && !tw.find_parent(AST_Scope);
2376 var arg_scope = null;
2377 var tw = new TreeWalker(function(node, descend) {
2378 if (!arg) return true;
2379 if (has_await(node) || node instanceof AST_Yield) {
2383 if (node instanceof AST_ObjectIdentity && (fn_strict || !arg_scope)) {
2387 if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
2388 var s = node.definition().scope;
2389 if (s !== scope) while (s = s.parent_scope) {
2390 if (s === scope) return true;
2394 if (node instanceof AST_Scope && !is_arrow(node)) {
2395 var save_scope = arg_scope;
2398 arg_scope = save_scope;
2402 args = iife.args.slice();
2403 var len = args.length;
2404 var names = Object.create(null);
2405 for (var i = fn.argnames.length; --i >= 0;) {
2406 var sym = fn.argnames[i];
2409 if (sym instanceof AST_DefaultValue) {
2412 args[len + i] = value;
2414 if (sym instanceof AST_Destructured) {
2415 if (!may_throw_destructured(sym, arg)) continue;
2416 candidates.length = 0;
2419 if (sym.name in names) continue;
2420 names[sym.name] = true;
2421 if (value) arg = !arg || is_undefined(arg) ? value : null;
2422 if (!arg && !value) {
2423 arg = make_node(AST_Undefined, sym).transform(compressor);
2424 } else if (arg instanceof AST_Lambda && arg.pinned()) {
2430 var candidate = make_node(AST_VarDef, sym, {
2434 candidate.name_index = i;
2435 candidate.arg_index = value ? len + i : i;
2436 candidates.unshift([ candidate ]);
2441 function extract_candidates(expr, unused) {
2442 hit_stack.push(expr);
2443 if (expr instanceof AST_Array) {
2444 expr.elements.forEach(function(node) {
2445 extract_candidates(node, unused);
2447 } else if (expr instanceof AST_Assign) {
2448 var lhs = expr.left;
2449 if (!(lhs instanceof AST_Destructured)) candidates.push(hit_stack.slice());
2450 extract_candidates(lhs);
2451 extract_candidates(expr.right);
2452 if (lhs instanceof AST_SymbolRef && expr.operator == "=") {
2453 assignments[lhs.name] = (assignments[lhs.name] || 0) + 1;
2455 } else if (expr instanceof AST_Await) {
2456 extract_candidates(expr.expression, unused);
2457 } else if (expr instanceof AST_Binary) {
2458 var lazy = lazy_op[expr.operator];
2461 && expr.operator != "??"
2462 && expr.right instanceof AST_Assign
2463 && expr.right.operator == "="
2464 && !(expr.right.left instanceof AST_Destructured)) {
2465 candidates.push(hit_stack.slice());
2467 extract_candidates(expr.left, !lazy && unused);
2468 extract_candidates(expr.right, unused);
2469 } else if (expr instanceof AST_Call) {
2470 extract_candidates(expr.expression);
2471 expr.args.forEach(extract_candidates);
2472 } else if (expr instanceof AST_Case) {
2473 extract_candidates(expr.expression);
2474 } else if (expr instanceof AST_Conditional) {
2475 extract_candidates(expr.condition);
2476 extract_candidates(expr.consequent, unused);
2477 extract_candidates(expr.alternative, unused);
2478 } else if (expr instanceof AST_Definitions) {
2479 expr.definitions.forEach(extract_candidates);
2480 } else if (expr instanceof AST_Dot) {
2481 extract_candidates(expr.expression);
2482 } else if (expr instanceof AST_DWLoop) {
2483 extract_candidates(expr.condition);
2484 if (!(expr.body instanceof AST_Block)) {
2485 extract_candidates(expr.body);
2487 } else if (expr instanceof AST_Exit) {
2488 if (expr.value) extract_candidates(expr.value);
2489 } else if (expr instanceof AST_For) {
2490 if (expr.init) extract_candidates(expr.init, true);
2491 if (expr.condition) extract_candidates(expr.condition);
2492 if (expr.step) extract_candidates(expr.step, true);
2493 if (!(expr.body instanceof AST_Block)) {
2494 extract_candidates(expr.body);
2496 } else if (expr instanceof AST_ForEnumeration) {
2497 extract_candidates(expr.object);
2498 if (!(expr.body instanceof AST_Block)) {
2499 extract_candidates(expr.body);
2501 } else if (expr instanceof AST_If) {
2502 extract_candidates(expr.condition);
2503 if (!(expr.body instanceof AST_Block)) {
2504 extract_candidates(expr.body);
2506 if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
2507 extract_candidates(expr.alternative);
2509 } else if (expr instanceof AST_Object) {
2510 expr.properties.forEach(function(prop) {
2511 hit_stack.push(prop);
2512 if (prop.key instanceof AST_Node) extract_candidates(prop.key);
2513 if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value, unused);
2516 } else if (expr instanceof AST_Sequence) {
2517 var end = expr.expressions.length - (unused ? 0 : 1);
2518 expr.expressions.forEach(function(node, index) {
2519 extract_candidates(node, index < end);
2521 } else if (expr instanceof AST_SimpleStatement) {
2522 extract_candidates(expr.body, true);
2523 } else if (expr instanceof AST_Spread) {
2524 extract_candidates(expr.expression);
2525 } else if (expr instanceof AST_Sub) {
2526 extract_candidates(expr.expression);
2527 extract_candidates(expr.property);
2528 } else if (expr instanceof AST_Switch) {
2529 extract_candidates(expr.expression);
2530 expr.body.forEach(extract_candidates);
2531 } else if (expr instanceof AST_Unary) {
2532 if (UNARY_POSTFIX[expr.operator]) {
2533 candidates.push(hit_stack.slice());
2535 extract_candidates(expr.expression);
2537 } else if (expr instanceof AST_VarDef) {
2538 if (expr.name instanceof AST_SymbolVar) {
2540 var def = expr.name.definition();
2541 if (def.references.length > def.replaced) {
2542 candidates.push(hit_stack.slice());
2545 declare_only[expr.name.name] = (declare_only[expr.name.name] || 0) + 1;
2548 if (expr.value) extract_candidates(expr.value);
2549 } else if (expr instanceof AST_Yield) {
2550 if (expr.expression) extract_candidates(expr.expression);
2555 function find_stop(node, level) {
2556 var parent = scanner.parent(level);
2557 if (parent instanceof AST_Array) return node;
2558 if (parent instanceof AST_Assign) return node;
2559 if (parent instanceof AST_Await) return node;
2560 if (parent instanceof AST_Binary) return node;
2561 if (parent instanceof AST_Call) return node;
2562 if (parent instanceof AST_Case) return node;
2563 if (parent instanceof AST_Conditional) return node;
2564 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2565 if (parent instanceof AST_Exit) return node;
2566 if (parent instanceof AST_If) return node;
2567 if (parent instanceof AST_IterationStatement) return node;
2568 if (parent instanceof AST_ObjectProperty) return node;
2569 if (parent instanceof AST_PropAccess) return node;
2570 if (parent instanceof AST_Sequence) {
2571 return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
2573 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2574 if (parent instanceof AST_Spread) return node;
2575 if (parent instanceof AST_Switch) return node;
2576 if (parent instanceof AST_Unary) return node;
2577 if (parent instanceof AST_VarDef) return node;
2578 if (parent instanceof AST_Yield) return node;
2582 function find_stop_logical(parent, op, level) {
2586 parent = scanner.parent(++level);
2587 } while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op
2588 || parent instanceof AST_Binary && parent.operator == op);
2592 function find_stop_expr(expr, cont, node, parent, level) {
2593 var replace = can_replace;
2594 can_replace = false;
2595 var after = stop_after;
2596 var if_hit = stop_if_hit;
2597 var stack = scanner.stack;
2598 scanner.stack = [ parent ];
2599 expr.transform(scanner);
2600 scanner.stack = stack;
2601 stop_if_hit = if_hit;
2603 can_replace = replace;
2608 return cont(parent, level + 1);
2611 function find_stop_value(node, level) {
2612 var parent = scanner.parent(level);
2613 if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
2614 if (parent instanceof AST_Assign) {
2615 if (may_throw(parent)) return node;
2616 if (parent.left.match_symbol(function(ref) {
2617 return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name);
2620 if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) {
2621 return find_stop_value(parent, level + 1);
2623 return find_stop_logical(parent, op, level);
2625 if (parent instanceof AST_Binary) {
2627 if (parent.left === node || !lazy_op[op = parent.operator]) {
2628 return find_stop_value(parent, level + 1);
2630 return find_stop_logical(parent, op, level);
2632 if (parent instanceof AST_Call) return parent;
2633 if (parent instanceof AST_Case) {
2634 if (parent.expression !== node) return node;
2635 return find_stop_value(parent, level + 1);
2637 if (parent instanceof AST_Conditional) {
2638 if (parent.condition !== node) return node;
2639 return find_stop_value(parent, level + 1);
2641 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2642 if (parent instanceof AST_Do) return node;
2643 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2644 if (parent instanceof AST_For) {
2645 if (parent.init !== node && parent.condition !== node) return node;
2646 return find_stop_value(parent, level + 1);
2648 if (parent instanceof AST_ForEnumeration) {
2649 if (parent.init !== node) return node;
2650 return find_stop_value(parent, level + 1);
2652 if (parent instanceof AST_If) {
2653 if (parent.condition !== node) return node;
2654 return find_stop_value(parent, level + 1);
2656 if (parent instanceof AST_ObjectProperty) {
2657 var obj = scanner.parent(level + 1);
2658 return all(obj.properties, function(prop) {
2659 return prop instanceof AST_ObjectKeyVal;
2660 }) ? find_stop_value(obj, level + 2) : obj;
2662 if (parent instanceof AST_PropAccess) {
2663 var exp = parent.expression;
2664 return exp === node ? find_stop_value(parent, level + 1) : node;
2666 if (parent instanceof AST_Sequence) {
2667 return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
2669 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2670 if (parent instanceof AST_Spread) return find_stop_value(parent, level + 1);
2671 if (parent instanceof AST_Switch) {
2672 if (parent.expression !== node) return node;
2673 return find_stop_value(parent, level + 1);
2675 if (parent instanceof AST_Unary) {
2676 if (parent.operator == "delete") return node;
2677 return find_stop_value(parent, level + 1);
2679 if (parent instanceof AST_VarDef) return parent.name.match_symbol(function(sym) {
2680 return sym instanceof AST_SymbolDeclaration && (lhs.name == sym.name || value_def.name == sym.name);
2681 }) ? node : find_stop_value(parent, level + 1);
2682 if (parent instanceof AST_While) {
2683 if (parent.condition !== node) return node;
2684 return find_stop_value(parent, level + 1);
2686 if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1);
2690 function find_stop_unused(node, level) {
2691 var parent = scanner.parent(level);
2692 if (is_last_node(node, parent)) return node;
2693 if (in_conditional(node, parent)) return node;
2694 if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
2695 if (parent instanceof AST_Assign) return check_assignment(parent.left);
2696 if (parent instanceof AST_Await) return node;
2697 if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
2698 if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
2699 if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
2700 if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
2701 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2702 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2703 if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
2704 if (parent instanceof AST_IterationStatement) return node;
2705 if (parent instanceof AST_ObjectProperty) {
2706 var obj = scanner.parent(level + 1);
2707 return all(obj.properties, function(prop) {
2708 return prop instanceof AST_ObjectKeyVal;
2709 }) ? find_stop_unused(obj, level + 2) : obj;
2711 if (parent instanceof AST_PropAccess) {
2712 var exp = parent.expression;
2713 if (exp === node) return find_stop_unused(parent, level + 1);
2714 return find_stop_expr(exp, find_stop_unused, node, parent, level);
2716 if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
2717 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2718 if (parent instanceof AST_Spread) return node;
2719 if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
2720 if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
2721 if (parent instanceof AST_VarDef) return check_assignment(parent.name);
2722 if (parent instanceof AST_Yield) return node;
2725 function check_assignment(lhs) {
2726 if (may_throw(parent)) return node;
2727 if (lhs !== node && lhs instanceof AST_Destructured) {
2728 return find_stop_expr(lhs, find_stop_unused, node, parent, level);
2730 return find_stop_unused(parent, level + 1);
2734 function mangleable_var(rhs) {
2736 force_single = false;
2739 if (remaining < 1) return;
2740 var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
2741 if (!(value instanceof AST_SymbolRef)) return;
2742 var def = value.definition();
2743 if (def.undeclared) return;
2744 if (is_arguments(def)) return;
2745 if (value !== rhs) {
2746 if (is_lhs_read_only(value, compressor)) return;
2747 var referenced = def.references.length - def.replaced;
2748 if (referenced < 2) return;
2749 var expr = candidate.clone();
2750 expr[expr instanceof AST_Assign ? "right" : "value"] = value;
2751 if (candidate.name_index >= 0) {
2752 expr.name_index = candidate.name_index;
2753 expr.arg_index = candidate.arg_index;
2757 return value_def = def;
2760 function remaining_refs(def) {
2761 return def.references.length - def.replaced - (assignments[def.name] || 0);
2764 function get_lhs(expr) {
2765 if (expr instanceof AST_Assign) {
2766 var lhs = expr.left;
2767 if (expr.operator != "=") return lhs;
2768 if (!(lhs instanceof AST_SymbolRef)) return lhs;
2769 var def = lhs.definition();
2770 if (scope.uses_arguments && is_funarg(def)) return lhs;
2771 if (compressor.exposed(def)) return lhs;
2772 remaining = remaining_refs(def);
2773 if (def.fixed && lhs.fixed) {
2774 var matches = def.references.filter(function(ref) {
2775 return ref.fixed === lhs.fixed;
2777 if (matches < remaining) {
2778 remaining = matches;
2782 mangleable_var(expr.right);
2785 if (expr instanceof AST_Binary) return expr.right.left;
2786 if (expr instanceof AST_Unary) return expr.expression;
2787 if (expr instanceof AST_VarDef) {
2788 var lhs = expr.name;
2789 var def = lhs.definition();
2790 if (def.const_redefs) return;
2791 if (!member(lhs, def.orig)) return;
2792 if (scope.uses_arguments && is_funarg(def)) return;
2793 var declared = def.orig.length - def.eliminated - (declare_only[def.name] || 0);
2794 remaining = remaining_refs(def);
2795 if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) {
2796 if (!ref.fixed) return true;
2797 if (!ref.fixed.assigns) return true;
2798 var assign = ref.fixed.assigns[0];
2799 return assign === lhs || get_rvalue(assign) === expr.value;
2801 if (declared > 1 && !(lhs instanceof AST_SymbolFunarg)) {
2802 mangleable_var(expr.value);
2803 return make_node(AST_SymbolRef, lhs, lhs);
2805 if (mangleable_var(expr.value) || remaining == 1 && !compressor.exposed(def)) {
2806 return make_node(AST_SymbolRef, lhs, lhs);
2812 function get_rvalue(expr) {
2813 if (expr instanceof AST_Assign) return expr.right;
2814 if (expr instanceof AST_Binary) {
2815 var node = expr.clone();
2816 node.right = expr.right.right;
2819 if (expr instanceof AST_VarDef) return expr.value;
2822 function invariant(expr) {
2823 if (expr instanceof AST_Array) return false;
2824 if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
2825 return invariant(expr.left) && invariant(expr.right);
2827 if (expr instanceof AST_Call) return false;
2828 if (expr instanceof AST_Conditional) {
2829 return invariant(expr.consequent) && invariant(expr.alternative);
2831 if (expr instanceof AST_Object) return false;
2832 return !expr.has_side_effects(compressor);
2835 function foldable(expr) {
2836 if (expr instanceof AST_Assign && expr.right.single_use) return;
2837 var lhs_ids = Object.create(null);
2838 var marker = new TreeWalker(function(node) {
2839 if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true;
2841 while (expr instanceof AST_Assign && expr.operator == "=") {
2842 expr.left.walk(marker);
2845 if (expr instanceof AST_ObjectIdentity) return rhs_exact_match;
2846 if (expr instanceof AST_SymbolRef) {
2847 var value = expr.evaluate(compressor);
2848 if (value === expr) return rhs_exact_match;
2849 return rhs_fuzzy_match(value, rhs_exact_match);
2851 if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
2852 if (expr.is_constant()) {
2853 var ev = expr.evaluate(compressor);
2854 if (!(ev instanceof AST_Node)) return rhs_fuzzy_match(ev, rhs_exact_match);
2856 if (!(lhs instanceof AST_SymbolRef)) return false;
2857 if (!invariant(expr)) return false;
2859 expr.walk(new TreeWalker(function(node) {
2860 if (circular) return true;
2861 if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true;
2863 return !circular && rhs_exact_match;
2865 function rhs_exact_match(node) {
2866 return expr.equivalent_to(node);
2870 function rhs_fuzzy_match(value, fallback) {
2871 return function(node, tw) {
2872 if (tw.in_boolean_context()) {
2873 if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
2876 if (node.is_constant()) {
2877 var ev = node.evaluate(compressor);
2878 if (!(ev instanceof AST_Node)) return !ev == !value;
2881 return fallback(node);
2885 function may_be_global(node) {
2886 if (node instanceof AST_SymbolRef) {
2887 node = node.fixed_value();
2888 if (!node) return true;
2890 if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
2891 return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity;
2894 function get_lvalues(expr) {
2895 var lvalues = new Dictionary();
2896 if (expr instanceof AST_VarDef) {
2897 if (!expr.name.definition().fixed) well_defined = false;
2898 lvalues.add(expr.name.name, lhs);
2900 var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict");
2901 var scan_toplevel = scope instanceof AST_Toplevel;
2902 var tw = new TreeWalker(function(node) {
2904 if (node instanceof AST_SymbolRef) {
2905 value = node.fixed_value();
2908 var def = node.definition();
2910 && (def.assignments || !def.escaped || def.escaped.cross_scope)
2911 && (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) {
2912 well_defined = false;
2915 } else if (node instanceof AST_ObjectIdentity) {
2918 if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
2919 if (find_arguments && node instanceof AST_Sub) {
2920 scope.each_argname(function(argname) {
2921 if (!compressor.option("reduce_vars") || argname.definition().assignments) {
2922 if (!argname.definition().fixed) well_defined = false;
2923 lvalues.add(argname.name, true);
2926 find_arguments = false;
2928 if (!scan_toplevel) return;
2929 if (node.TYPE == "Call") {
2930 if (modify_toplevel) return;
2931 var exp = node.expression;
2932 if (exp instanceof AST_PropAccess) return;
2933 if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return;
2934 modify_toplevel = true;
2935 } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
2936 if (node === lhs && !(expr instanceof AST_Unary)) {
2937 modify_toplevel = true;
2939 read_toplevel = true;
2947 function remove_candidate(expr) {
2948 var index = expr.name_index;
2950 var argname = scope.argnames[index];
2951 if (argname instanceof AST_DefaultValue) {
2952 argname.value = make_node(AST_Number, argname, {
2955 argname.name.definition().fixed = false;
2957 var args = compressor.parent().args;
2959 args[index] = make_node(AST_Number, args[index], {
2962 argname.definition().fixed = false;
2967 var end = hit_stack.length - 1;
2968 if (hit_stack[end - 1].body === hit_stack[end]) end--;
2969 var tt = new TreeTransformer(function(node, descend, in_list) {
2970 if (hit) return node;
2971 if (node !== hit_stack[hit_index]) return node;
2973 if (hit_index <= end) return handle_custom_scan_order(node, tt);
2975 if (node instanceof AST_VarDef) {
2976 declare_only[node.name.name] = (declare_only[node.name.name] || 0) + 1;
2977 if (value_def) value_def.replaced++;
2978 node = node.clone();
2982 return in_list ? List.skip : null;
2987 return statements[stat_index].transform(tt);
2990 function patch_sequence(node) {
2991 if (node instanceof AST_Sequence) switch (node.expressions.length) {
2992 case 0: return null;
2993 case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]);
2997 function is_lhs_local(lhs) {
2998 var sym = root_expr(lhs);
2999 return sym instanceof AST_SymbolRef
3000 && sym.definition().scope.resolve() === scope
3002 && (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
3003 || candidate instanceof AST_Unary
3004 || candidate instanceof AST_Assign && candidate.operator != "="));
3007 function value_has_side_effects() {
3008 if (candidate instanceof AST_Unary) return false;
3009 return rvalue.has_side_effects(compressor);
3012 function replace_all_symbols(expr) {
3013 if (expr instanceof AST_Unary) return false;
3014 if (side_effects) return false;
3015 if (value_def) return true;
3016 if (!(lhs instanceof AST_SymbolRef)) return false;
3018 if (expr instanceof AST_VarDef) {
3020 } else if (expr.operator == "=") {
3025 var def = lhs.definition();
3026 return def.references.length - def.replaced == referenced;
3029 function symbol_in_lvalues(sym, parent) {
3030 var lvalue = lvalues.get(sym.name);
3031 if (!lvalue || all(lvalue, function(lhs) {
3034 if (lvalue[0] !== lhs) return true;
3038 function may_modify(sym) {
3039 var def = sym.definition();
3040 if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
3041 if (def.scope.resolve() !== scope) return true;
3042 if (modify_toplevel && compressor.exposed(def)) return true;
3043 return !all(def.references, function(ref) {
3044 return ref.scope.resolve() === scope;
3048 function side_effects_external(node, lhs) {
3049 if (node instanceof AST_Assign) return side_effects_external(node.left, true);
3050 if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
3051 if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
3053 if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
3054 if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
3055 if (node instanceof AST_SymbolRef) return node.definition().scope.resolve() !== scope;
3061 function eliminate_spurious_blocks(statements) {
3063 for (var i = 0; i < statements.length;) {
3064 var stat = statements[i];
3065 if (stat instanceof AST_BlockStatement) {
3066 if (all(stat.body, safe_to_trim)) {
3068 eliminate_spurious_blocks(stat.body);
3069 [].splice.apply(statements, [i, 1].concat(stat.body));
3070 i += stat.body.length;
3074 if (stat instanceof AST_Directive) {
3075 if (member(stat.value, seen_dirs)) {
3077 statements.splice(i, 1);
3080 seen_dirs.push(stat.value);
3082 if (stat instanceof AST_EmptyStatement) {
3084 statements.splice(i, 1);
3091 function handle_if_return(statements, compressor) {
3092 var self = compressor.self();
3093 var parent = compressor.parent();
3094 var in_lambda = last_of(function(node) {
3095 return node instanceof AST_Lambda;
3097 var in_iife = in_lambda && parent && parent.TYPE == "Call";
3098 var multiple_if_returns = has_multiple_if_returns(statements);
3099 for (var i = statements.length; --i >= 0;) {
3100 var stat = statements[i];
3101 var j = next_index(i);
3102 var next = statements[j];
3104 if (in_lambda && !next && stat instanceof AST_Return) {
3107 statements.splice(i, 1);
3110 var tail = stat.value.tail_node();
3111 if (tail instanceof AST_UnaryPrefix && tail.operator == "void") {
3114 if (tail === stat.value) {
3115 body = tail.expression;
3117 body = stat.value.clone();
3118 body.expressions[body.length - 1] = tail.expression;
3120 statements[i] = make_node(AST_SimpleStatement, stat, {
3127 if (stat instanceof AST_If) {
3128 var ab = aborts(stat.body);
3129 if (can_merge_flow(ab)) {
3130 if (ab.label) remove(ab.label.thedef.references, ab);
3132 stat = stat.clone();
3133 stat.condition = stat.condition.negate(compressor);
3134 var body = as_statement_array_with_return(stat.body, ab);
3135 stat.body = make_node(AST_BlockStatement, stat, {
3136 body: as_statement_array(stat.alternative).concat(extract_functions())
3138 stat.alternative = make_node(AST_BlockStatement, stat, {
3141 statements[i] = stat;
3142 statements[i] = stat.transform(compressor);
3146 if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) {
3147 var negated = stat.condition.negate(compressor);
3148 if (negated.print_to_string().length <= stat.condition.print_to_string().length) {
3150 stat = stat.clone();
3151 stat.condition = negated;
3152 statements[j] = stat.body;
3154 statements[i] = stat;
3155 statements[i] = stat.transform(compressor);
3160 var alt = aborts(stat.alternative);
3161 if (can_merge_flow(alt)) {
3162 if (alt.label) remove(alt.label.thedef.references, alt);
3164 stat = stat.clone();
3165 stat.body = make_node(AST_BlockStatement, stat.body, {
3166 body: as_statement_array(stat.body).concat(extract_functions())
3168 var body = as_statement_array_with_return(stat.alternative, alt);
3169 stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
3172 statements[i] = stat;
3173 statements[i] = stat.transform(compressor);
3177 if (compressor.option("typeofs")) {
3179 mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
3180 body: statements.slice(i + 1)
3184 mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
3185 body: statements.slice(i + 1)
3191 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3192 var value = stat.body.value;
3193 var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
3195 // pretty silly case, but:
3196 // if (foo()) return; return; ---> foo(); return;
3197 if (!value && !stat.alternative
3198 && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
3200 statements[i] = make_node(AST_SimpleStatement, stat.condition, {
3201 body: stat.condition
3206 // if (foo()) return x; return y; ---> return foo() ? x : y;
3207 if (!stat.alternative && next instanceof AST_Return) {
3209 stat = stat.clone();
3210 stat.alternative = next;
3211 statements.splice(i, 1, stat.transform(compressor));
3212 statements.splice(j, 1);
3216 // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined;
3217 if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
3219 stat = stat.clone();
3220 stat.alternative = make_node(AST_Return, stat, {
3223 statements.splice(i, 1, stat.transform(compressor));
3227 // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e;
3229 // if sequences is not enabled, this can lead to an endless loop (issue #866).
3230 // however, with sequences on this helps producing slightly better output for
3231 // the example code.
3232 var prev = statements[prev_index(i)];
3233 if (compressor.option("sequences") && in_lambda && !stat.alternative
3234 && (!prev && in_iife || prev instanceof AST_If && prev.body instanceof AST_Return)
3235 && next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
3237 stat = stat.clone();
3238 stat.alternative = make_node(AST_BlockStatement, next, {
3241 make_node(AST_Return, next, {
3246 statements.splice(i, 1, stat.transform(compressor));
3247 statements.splice(j, 1);
3253 function has_multiple_if_returns(statements) {
3255 for (var i = statements.length; --i >= 0;) {
3256 var stat = statements[i];
3257 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3258 if (++n > 1) return true;
3264 function is_return_void(value) {
3265 return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
3268 function last_of(predicate) {
3269 var block = self, stat, level = 0;
3272 if (predicate(block)) return true;
3273 block = compressor.parent(level++);
3274 } while (block instanceof AST_If && (stat = block));
3275 } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
3276 && is_last_statement(block.body, stat));
3279 function match_target(target) {
3280 return last_of(function(node) {
3281 return node === target;
3285 function can_drop_abort(ab) {
3286 if (ab instanceof AST_Return) return in_lambda && is_return_void(ab.value);
3287 if (!(ab instanceof AST_LoopControl)) return false;
3288 var lct = compressor.loopcontrol_target(ab);
3289 if (ab instanceof AST_Continue) return match_target(loop_body(lct));
3290 if (lct instanceof AST_IterationStatement) return false;
3291 return match_target(lct);
3294 function can_merge_flow(ab) {
3295 if (!can_drop_abort(ab)) return false;
3296 for (var j = statements.length; --j > i;) {
3297 var stat = statements[j];
3298 if (stat instanceof AST_DefClass) {
3299 if (stat.name.definition().preinit) return false;
3300 } else if (stat instanceof AST_Const || stat instanceof AST_Let) {
3301 if (!all(stat.definitions, function(defn) {
3302 return !defn.name.match_symbol(function(node) {
3303 return node instanceof AST_SymbolDeclaration && node.definition().preinit;
3311 function extract_functions() {
3313 var lexical = false;
3314 var tail = statements.splice(i + 1).filter(function(stat) {
3315 if (stat instanceof AST_LambdaDefinition) {
3319 if (is_lexical_definition(stat)) lexical = true;
3322 [].push.apply(lexical ? tail : statements, defuns);
3326 function as_statement_array_with_return(node, ab) {
3327 var body = as_statement_array(node);
3328 var block = body, last;
3329 while ((last = block[block.length - 1]) !== ab) {
3333 if (ab.value) block.push(make_node(AST_SimpleStatement, ab.value, {
3334 body: ab.value.expression
3339 function next_index(i) {
3340 for (var j = i + 1; j < statements.length; j++) {
3341 if (!is_declaration(statements[j])) break;
3346 function prev_index(i) {
3347 for (var j = i; --j >= 0;) {
3348 if (!is_declaration(statements[j])) break;
3354 function eliminate_dead_code(statements, compressor) {
3356 var self = compressor.self();
3357 for (var i = 0, n = 0, len = statements.length; i < len; i++) {
3358 var stat = statements[i];
3359 if (stat instanceof AST_LoopControl) {
3360 var lct = compressor.loopcontrol_target(stat);
3361 if (loop_body(lct) !== self
3362 || stat instanceof AST_Break && lct instanceof AST_IterationStatement) {
3363 statements[n++] = stat;
3364 } else if (stat.label) {
3365 remove(stat.label.thedef.references, stat);
3368 statements[n++] = stat;
3371 has_quit = statements.slice(i + 1);
3375 statements.length = n;
3376 if (has_quit) has_quit.forEach(function(stat) {
3377 extract_declarations_from_unreachable_code(compressor, stat, statements);
3379 CHANGED = statements.length != len;
3382 function sequencesize(statements, compressor) {
3383 if (statements.length < 2) return;
3384 var seq = [], n = 0;
3385 function push_seq() {
3386 if (!seq.length) return;
3387 var body = make_sequence(seq[0], seq);
3388 statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
3391 for (var i = 0, len = statements.length; i < len; i++) {
3392 var stat = statements[i];
3393 if (stat instanceof AST_SimpleStatement) {
3394 if (seq.length >= compressor.sequences_limit) push_seq();
3395 var body = stat.body;
3396 if (seq.length > 0) body = body.drop_side_effect_free(compressor);
3397 if (body) merge_sequence(seq, body);
3398 } else if (is_declaration(stat)) {
3399 statements[n++] = stat;
3402 statements[n++] = stat;
3406 statements.length = n;
3407 if (n != len) CHANGED = true;
3410 function to_simple_statement(block, decls) {
3411 if (!(block instanceof AST_BlockStatement)) return block;
3413 for (var i = 0; i < block.body.length; i++) {
3414 var line = block.body[i];
3415 if (line instanceof AST_Var && declarations_only(line)) {
3417 } else if (stat || is_lexical_definition(line)) {
3426 function sequencesize_2(statements, compressor) {
3427 function cons_seq(right) {
3430 var left = prev.body;
3431 return make_sequence(left, [ left, right ]);
3434 for (var i = 0; i < statements.length; i++) {
3435 var stat = statements[i];
3437 if (stat instanceof AST_Exit) {
3438 if (stat.value || !in_async_generator(scope)) {
3439 stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).optimize(compressor);
3441 } else if (stat instanceof AST_For) {
3442 if (!(stat.init instanceof AST_Definitions)) {
3444 prev.body.walk(new TreeWalker(function(node) {
3445 if (abort || node instanceof AST_Scope) return true;
3446 if (node instanceof AST_Binary && node.operator == "in") {
3452 if (stat.init) stat.init = cons_seq(stat.init);
3454 stat.init = prev.body;
3460 } else if (stat instanceof AST_ForIn) {
3461 if (!is_lexical_definition(stat.init)) stat.object = cons_seq(stat.object);
3462 } else if (stat instanceof AST_If) {
3463 stat.condition = cons_seq(stat.condition);
3464 } else if (stat instanceof AST_Switch) {
3465 stat.expression = cons_seq(stat.expression);
3466 } else if (stat instanceof AST_With) {
3467 stat.expression = cons_seq(stat.expression);
3470 if (compressor.option("conditionals") && stat instanceof AST_If) {
3472 var body = to_simple_statement(stat.body, decls);
3473 var alt = to_simple_statement(stat.alternative, decls);
3474 if (body !== false && alt !== false && decls.length > 0) {
3475 var len = decls.length;
3476 decls.push(make_node(AST_If, stat, {
3477 condition: stat.condition,
3478 body: body || make_node(AST_EmptyStatement, stat.body),
3481 decls.unshift(n, 1);
3482 [].splice.apply(statements, decls);
3490 statements[n++] = stat;
3491 prev = stat instanceof AST_SimpleStatement ? stat : null;
3493 statements.length = n;
3496 function extract_exprs(body) {
3497 if (body instanceof AST_Assign) return [ body ];
3498 if (body instanceof AST_Sequence) return body.expressions.slice();
3501 function join_assigns(defn, body, keep) {
3502 var exprs = extract_exprs(body);
3504 var trimmed = false;
3505 for (var i = exprs.length - (keep || 0); --i >= 0;) {
3506 var expr = exprs[i];
3507 if (!can_trim(expr)) continue;
3509 if (expr.left instanceof AST_SymbolRef) {
3510 tail = exprs.slice(i + 1);
3511 } else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) {
3512 tail = exprs.slice(i + 1);
3513 var flattened = expr.clone();
3514 expr = expr.left.expression;
3515 flattened.left = flattened.left.clone();
3516 flattened.left.expression = expr.left.clone();
3517 tail.unshift(flattened);
3521 if (tail.length == 0) continue;
3522 if (!trim_assigns(expr.left, expr.right, tail)) continue;
3524 exprs = exprs.slice(0, i).concat(expr, tail);
3526 if (defn instanceof AST_Definitions) {
3528 for (var i = defn.definitions.length; --i >= 0;) {
3529 var def = defn.definitions[i];
3530 if (!def.value) continue;
3531 if (trim_assigns(def.name, def.value, exprs)) trimmed = true;
3532 if (merge_conditional_assignments(def, exprs, keep)) trimmed = true;
3535 if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
3537 return trimmed && exprs;
3539 function can_trim(node) {
3540 return node instanceof AST_Assign && node.operator == "=";
3544 function merge_assigns(prev, defn) {
3545 if (!(prev instanceof AST_SimpleStatement)) return;
3546 if (declarations_only(defn)) return;
3547 var exprs = extract_exprs(prev.body);
3549 var definitions = [];
3550 if (!join_var_assign(definitions, exprs.reverse(), 0)) return;
3551 defn.definitions = definitions.reverse().concat(defn.definitions);
3552 return exprs.reverse();
3555 function merge_conditional_assignments(var_def, exprs, keep) {
3556 if (!compressor.option("conditionals")) return;
3557 if (var_def.name instanceof AST_Destructured) return;
3558 var trimmed = false;
3559 var def = var_def.name.definition();
3560 while (exprs.length > keep) {
3561 var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]);
3563 var_def.value = cond;
3570 function join_var_assign(definitions, exprs, keep) {
3571 var trimmed = false;
3572 while (exprs.length > keep) {
3573 var expr = exprs[0];
3574 if (!(expr instanceof AST_Assign)) break;
3575 if (expr.operator != "=") break;
3576 var lhs = expr.left;
3577 if (!(lhs instanceof AST_SymbolRef)) break;
3578 if (is_undeclared_ref(lhs)) break;
3579 if (lhs.scope.resolve() !== scope) break;
3580 var def = lhs.definition();
3581 if (def.scope !== scope) break;
3582 if (def.orig.length > def.eliminated + 1) break;
3583 if (def.orig[0].TYPE != "SymbolVar") break;
3584 var name = make_node(AST_SymbolVar, lhs, lhs);
3585 definitions.push(make_node(AST_VarDef, expr, {
3589 def.orig.push(name);
3597 function trim_assigns(name, value, exprs) {
3598 var names = Object.create(null);
3599 names[name.name] = true;
3600 while (value instanceof AST_Assign && value.operator == "=") {
3601 if (value.left instanceof AST_SymbolRef) names[value.left.name] = true;
3602 value = value.right;
3604 if (!(value instanceof AST_Object)) return;
3605 var trimmed = false;
3607 if (!try_join(exprs[0])) break;
3610 } while (exprs.length);
3613 function try_join(node) {
3614 if (!(node instanceof AST_Assign)) return;
3615 if (node.operator != "=") return;
3616 if (!(node.left instanceof AST_PropAccess)) return;
3617 var sym = node.left.expression;
3618 if (!(sym instanceof AST_SymbolRef)) return;
3619 if (!(sym.name in names)) return;
3620 if (!node.right.is_constant_expression(scope)) return;
3621 var prop = node.left.property;
3622 if (prop instanceof AST_Node) {
3623 if (try_join(prop)) prop = node.left.property = prop.right.clone();
3624 prop = prop.evaluate(compressor);
3626 if (prop instanceof AST_Node) return;
3628 var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
3630 return typeof key == "string" && key != prop && key != "__proto__";
3631 } : function(node) {
3633 if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) {
3634 return typeof key == "string" && key != prop;
3636 return key !== "__proto__";
3638 if (!all(value.properties, diff)) return;
3639 value.properties.push(make_node(AST_ObjectKeyVal, node, {
3647 function join_consecutive_vars(statements) {
3649 for (var i = 0, j = -1; i < statements.length; i++) {
3650 var stat = statements[i];
3651 var prev = statements[j];
3652 if (stat instanceof AST_Definitions) {
3653 if (prev && prev.TYPE == stat.TYPE) {
3654 prev.definitions = prev.definitions.concat(stat.definitions);
3656 } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
3657 defs.definitions = defs.definitions.concat(stat.definitions);
3659 } else if (stat instanceof AST_Var) {
3660 var exprs = merge_assigns(prev, stat);
3663 prev.body = make_sequence(prev, exprs);
3670 statements[j] = defs = stat;
3672 statements[++j] = stat;
3675 } else if (stat instanceof AST_Exit) {
3676 stat.value = join_assigns_expr(stat.value);
3677 } else if (stat instanceof AST_For) {
3678 var exprs = join_assigns(prev, stat.init);
3681 stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
3682 } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
3684 prev.definitions = prev.definitions.concat(stat.init.definitions);
3686 defs = stat.init = prev;
3687 statements[j] = merge_defns(stat);
3690 } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
3691 defs.definitions = defs.definitions.concat(stat.init.definitions);
3694 } else if (stat.init instanceof AST_Var) {
3696 exprs = merge_assigns(prev, stat.init);
3699 if (exprs.length == 0) {
3700 statements[j] = merge_defns(stat);
3703 prev.body = make_sequence(prev, exprs);
3706 } else if (stat instanceof AST_ForEnumeration) {
3707 if (defs && defs.TYPE == stat.init.TYPE) {
3708 var defns = defs.definitions.slice();
3709 stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) {
3710 defns.push(make_node(AST_VarDef, name, {
3714 name.definition().references.push(ref);
3716 defs.definitions = defns;
3719 stat.object = join_assigns_expr(stat.object);
3720 } else if (stat instanceof AST_If) {
3721 stat.condition = join_assigns_expr(stat.condition);
3722 } else if (stat instanceof AST_SimpleStatement) {
3723 var exprs = join_assigns(prev, stat.body);
3726 if (!exprs.length) continue;
3727 stat.body = make_sequence(stat.body, exprs);
3729 } else if (stat instanceof AST_Switch) {
3730 stat.expression = join_assigns_expr(stat.expression);
3731 } else if (stat instanceof AST_With) {
3732 stat.expression = join_assigns_expr(stat.expression);
3734 statements[++j] = defs ? merge_defns(stat) : stat;
3736 statements.length = j + 1;
3738 function join_assigns_expr(value) {
3739 var exprs = join_assigns(prev, value, 1);
3740 if (!exprs) return value;
3742 var tail = value.tail_node();
3743 if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left);
3744 return make_sequence(value, exprs);
3747 function merge_defns(stat) {
3748 return stat.transform(new TreeTransformer(function(node, descend, in_list) {
3749 if (node instanceof AST_Definitions) {
3750 if (defs === node) return node;
3751 if (defs.TYPE != node.TYPE) return node;
3752 var parent = this.parent();
3753 if (parent instanceof AST_ForEnumeration && parent.init === node) return node;
3754 if (!declarations_only(node)) return node;
3755 defs.definitions = defs.definitions.concat(node.definitions);
3757 if (parent instanceof AST_For && parent.init === node) return null;
3758 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
3760 if (node instanceof AST_ExportDeclaration) return node;
3761 if (node instanceof AST_Scope) return node;
3762 if (!is_statement(node)) return node;
3768 function extract_declarations_from_unreachable_code(compressor, stat, target) {
3770 var dropped = false;
3771 stat.walk(new TreeWalker(function(node, descend) {
3772 if (node instanceof AST_DefClass) {
3773 node.extends = null;
3774 node.properties = [];
3778 if (node instanceof AST_Definitions) {
3780 if (node.remove_initializers(compressor, defns)) {
3781 AST_Node.warn("Dropping initialization in unreachable code [{file}:{line},{col}]", node.start);
3783 if (defns.length > 0) {
3784 node.definitions = defns;
3789 if (node instanceof AST_LambdaDefinition) {
3793 if (node instanceof AST_Scope) return true;
3794 if (node instanceof AST_BlockScope) {
3798 if (block.required) {
3799 target.push(make_node(AST_BlockStatement, stat, { body: block }));
3800 } else if (block.length) {
3801 [].push.apply(target, block);
3806 if (!(node instanceof AST_LoopControl)) dropped = true;
3808 if (dropped) AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
3810 function push(node) {
3813 if (!safe_to_trim(node)) block.required = true;
3820 function is_undefined(node, compressor) {
3821 return node.is_undefined
3822 || node instanceof AST_Undefined
3823 || node instanceof AST_UnaryPrefix
3824 && node.operator == "void"
3825 && !(compressor && node.expression.has_side_effects(compressor));
3829 // return true if `!!node === true`
3831 def(AST_Node, return_false);
3832 def(AST_Array, return_true);
3833 def(AST_Assign, function() {
3834 return this.operator == "=" && this.right.is_truthy();
3836 def(AST_Lambda, return_true);
3837 def(AST_Object, return_true);
3838 def(AST_RegExp, return_true);
3839 def(AST_Sequence, function() {
3840 return this.tail_node().is_truthy();
3842 def(AST_SymbolRef, function() {
3843 var fixed = this.fixed_value();
3844 if (!fixed) return false;
3845 this.is_truthy = return_false;
3846 var result = fixed.is_truthy();
3847 delete this.is_truthy;
3850 })(function(node, func) {
3851 node.DEFMETHOD("is_truthy", func);
3854 // is_negative_zero()
3855 // return true if the node may represent -0
3857 def(AST_Node, return_true);
3858 def(AST_Array, return_false);
3859 function binary(op, left, right) {
3862 return left.is_negative_zero()
3863 && (!(right instanceof AST_Constant) || right.value == 0);
3866 return left.is_negative_zero() || right.is_negative_zero();
3876 def(AST_Assign, function() {
3877 var op = this.operator;
3878 if (op == "=") return this.right.is_negative_zero();
3879 return binary(op.slice(0, -1), this.left, this.right);
3881 def(AST_Binary, function() {
3882 return binary(this.operator, this.left, this.right);
3884 def(AST_Constant, function() {
3885 return this.value == 0 && 1 / this.value < 0;
3887 def(AST_Lambda, return_false);
3888 def(AST_Object, return_false);
3889 def(AST_RegExp, return_false);
3890 def(AST_Sequence, function() {
3891 return this.tail_node().is_negative_zero();
3893 def(AST_SymbolRef, function() {
3894 var fixed = this.fixed_value();
3895 if (!fixed) return true;
3896 this.is_negative_zero = return_true;
3897 var result = fixed.is_negative_zero();
3898 delete this.is_negative_zero;
3901 def(AST_UnaryPrefix, function() {
3902 return this.operator == "+" && this.expression.is_negative_zero()
3903 || this.operator == "-";
3905 })(function(node, func) {
3906 node.DEFMETHOD("is_negative_zero", func);
3909 // may_throw_on_access()
3910 // returns true if this node may be null, undefined or contain `AST_Accessor`
3912 AST_Node.DEFMETHOD("may_throw_on_access", function(compressor, force) {
3913 return !compressor.option("pure_getters") || this._dot_throw(compressor, force);
3915 function is_strict(compressor, force) {
3916 return force || /strict/.test(compressor.option("pure_getters"));
3918 def(AST_Node, is_strict);
3919 def(AST_Array, return_false);
3920 def(AST_Assign, function(compressor) {
3921 var op = this.operator;
3922 var sym = this.left;
3923 var rhs = this.right;
3925 return lazy_op[op.slice(0, -1)] && (sym._dot_throw(compressor) || rhs._dot_throw(compressor));
3927 if (!rhs._dot_throw(compressor)) return false;
3928 if (!(sym instanceof AST_SymbolRef)) return true;
3929 if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
3930 return rhs.right._dot_throw(compressor);
3934 def(AST_Binary, function(compressor) {
3935 return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
3937 def(AST_Class, return_false);
3938 def(AST_Conditional, function(compressor) {
3939 return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
3941 def(AST_Constant, return_false);
3942 def(AST_Dot, function(compressor, force) {
3943 if (!is_strict(compressor, force)) return false;
3944 var exp = this.expression;
3945 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
3946 return !(this.property == "prototype" && is_lambda(exp));
3948 def(AST_Lambda, return_false);
3949 def(AST_Null, return_true);
3950 def(AST_Object, function(compressor, force) {
3951 return is_strict(compressor, force) && !all(this.properties, function(prop) {
3952 if (!(prop instanceof AST_ObjectKeyVal)) return false;
3953 return !(prop.key === "__proto__" && prop.value._dot_throw(compressor, force));
3956 def(AST_ObjectIdentity, function(compressor, force) {
3957 return is_strict(compressor, force) && !this.scope.resolve().new;
3959 def(AST_Sequence, function(compressor) {
3960 return this.tail_node()._dot_throw(compressor);
3962 def(AST_SymbolRef, function(compressor, force) {
3963 if (this.is_undefined) return true;
3964 if (!is_strict(compressor, force)) return false;
3965 if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
3966 if (this.is_immutable()) return false;
3967 var def = this.definition();
3968 if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) {
3969 return argname instanceof AST_SymbolFunarg;
3970 })) return def.scope.uses_arguments > 2;
3971 var fixed = this.fixed_value();
3972 if (!fixed) return true;
3973 this._dot_throw = return_true;
3974 if (fixed._dot_throw(compressor)) {
3975 delete this._dot_throw;
3978 this._dot_throw = return_false;
3981 def(AST_UnaryPrefix, function() {
3982 return this.operator == "void";
3984 def(AST_UnaryPostfix, return_false);
3985 def(AST_Undefined, return_true);
3986 })(function(node, func) {
3987 node.DEFMETHOD("_dot_throw", func);
3991 def(AST_Node, return_false);
3992 def(AST_Array, return_true);
3993 function is_binary_defined(compressor, op, node) {
3996 return node.left.is_defined(compressor) && node.right.is_defined(compressor);
3998 return node.left.is_truthy() || node.right.is_defined(compressor);
4000 return node.left.is_defined(compressor) || node.right.is_defined(compressor);
4005 def(AST_Assign, function(compressor) {
4006 var op = this.operator;
4007 if (op == "=") return this.right.is_defined(compressor);
4008 return is_binary_defined(compressor, op.slice(0, -1), this);
4010 def(AST_Binary, function(compressor) {
4011 return is_binary_defined(compressor, this.operator, this);
4013 def(AST_Conditional, function(compressor) {
4014 return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
4016 def(AST_Constant, return_true);
4017 def(AST_Hole, return_false);
4018 def(AST_Lambda, return_true);
4019 def(AST_Object, return_true);
4020 def(AST_Sequence, function(compressor) {
4021 return this.tail_node().is_defined(compressor);
4023 def(AST_SymbolRef, function(compressor) {
4024 if (this.is_undefined) return false;
4025 if (is_undeclared_ref(this) && this.is_declared(compressor)) return true;
4026 if (this.is_immutable()) return true;
4027 var fixed = this.fixed_value();
4028 if (!fixed) return false;
4029 this.is_defined = return_false;
4030 var result = fixed.is_defined(compressor);
4031 delete this.is_defined;
4034 def(AST_UnaryPrefix, function() {
4035 return this.operator != "void";
4037 def(AST_UnaryPostfix, return_true);
4038 def(AST_Undefined, return_false);
4039 })(function(node, func) {
4040 node.DEFMETHOD("is_defined", func);
4043 /* -----[ boolean/negation helpers ]----- */
4045 // methods to determine whether an expression has a boolean result type
4047 def(AST_Node, return_false);
4048 def(AST_Assign, function(compressor) {
4049 return this.operator == "=" && this.right.is_boolean(compressor);
4051 var binary = makePredicate("in instanceof == != === !== < <= >= >");
4052 def(AST_Binary, function(compressor) {
4053 return binary[this.operator] || lazy_op[this.operator]
4054 && this.left.is_boolean(compressor)
4055 && this.right.is_boolean(compressor);
4057 def(AST_Boolean, return_true);
4058 var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some");
4059 def(AST_Call, function(compressor) {
4060 if (!compressor.option("unsafe")) return false;
4061 var exp = this.expression;
4062 return exp instanceof AST_Dot && (fn[exp.property]
4063 || exp.property == "test" && exp.expression instanceof AST_RegExp);
4065 def(AST_Conditional, function(compressor) {
4066 return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
4068 def(AST_New, return_false);
4069 def(AST_Sequence, function(compressor) {
4070 return this.tail_node().is_boolean(compressor);
4072 def(AST_SymbolRef, function(compressor) {
4073 var fixed = this.fixed_value();
4074 if (!fixed) return false;
4075 this.is_boolean = return_false;
4076 var result = fixed.is_boolean(compressor);
4077 delete this.is_boolean;
4080 var unary = makePredicate("! delete");
4081 def(AST_UnaryPrefix, function() {
4082 return unary[this.operator];
4084 })(function(node, func) {
4085 node.DEFMETHOD("is_boolean", func);
4088 // methods to determine if an expression has a numeric result type
4090 def(AST_Node, return_false);
4091 var binary = makePredicate("- * / % ** & | ^ << >> >>>");
4092 def(AST_Assign, function(compressor) {
4093 return binary[this.operator.slice(0, -1)]
4094 || this.operator == "=" && this.right.is_number(compressor);
4096 def(AST_Binary, function(compressor) {
4097 if (binary[this.operator]) return true;
4098 if (this.operator != "+") return false;
4099 return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
4100 && (this.right.is_boolean(compressor) || this.right.is_number(compressor));
4102 var fn = makePredicate([
4113 "getTimezoneOffset",
4118 "getUTCMilliseconds",
4139 "setUTCMilliseconds",
4148 def(AST_Call, function(compressor) {
4149 if (!compressor.option("unsafe")) return false;
4150 var exp = this.expression;
4151 return exp instanceof AST_Dot && (fn[exp.property]
4152 || is_undeclared_ref(exp.expression) && exp.expression.name == "Math");
4154 def(AST_Conditional, function(compressor) {
4155 return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
4157 def(AST_New, return_false);
4158 def(AST_Number, return_true);
4159 def(AST_Sequence, function(compressor) {
4160 return this.tail_node().is_number(compressor);
4162 def(AST_SymbolRef, function(compressor) {
4163 var fixed = this.fixed_value();
4164 if (!fixed) return false;
4165 this.is_number = return_false;
4166 var result = fixed.is_number(compressor);
4167 delete this.is_number;
4170 var unary = makePredicate("+ - ~ ++ --");
4171 def(AST_Unary, function() {
4172 return unary[this.operator];
4174 })(function(node, func) {
4175 node.DEFMETHOD("is_number", func);
4178 // methods to determine if an expression has a string result type
4180 def(AST_Node, return_false);
4181 def(AST_Assign, function(compressor) {
4182 switch (this.operator) {
4184 if (this.left.is_string(compressor)) return true;
4186 return this.right.is_string(compressor);
4189 def(AST_Binary, function(compressor) {
4190 return this.operator == "+" &&
4191 (this.left.is_string(compressor) || this.right.is_string(compressor));
4193 var fn = makePredicate([
4202 def(AST_Call, function(compressor) {
4203 if (!compressor.option("unsafe")) return false;
4204 var exp = this.expression;
4205 return exp instanceof AST_Dot && fn[exp.property];
4207 def(AST_Conditional, function(compressor) {
4208 return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
4210 def(AST_Sequence, function(compressor) {
4211 return this.tail_node().is_string(compressor);
4213 def(AST_String, return_true);
4214 def(AST_SymbolRef, function(compressor) {
4215 var fixed = this.fixed_value();
4216 if (!fixed) return false;
4217 this.is_string = return_false;
4218 var result = fixed.is_string(compressor);
4219 delete this.is_string;
4222 def(AST_Template, function(compressor) {
4223 return !this.tag || is_raw_tag(compressor, this.tag);
4225 def(AST_UnaryPrefix, function() {
4226 return this.operator == "typeof";
4228 })(function(node, func) {
4229 node.DEFMETHOD("is_string", func);
4232 var lazy_op = makePredicate("&& || ??");
4235 function to_node(value, orig) {
4236 if (value instanceof AST_Node) return value.clone(true);
4237 if (Array.isArray(value)) return make_node(AST_Array, orig, {
4238 elements: value.map(function(value) {
4239 return to_node(value, orig);
4242 if (value && typeof value == "object") {
4244 for (var key in value) if (HOP(value, key)) {
4245 props.push(make_node(AST_ObjectKeyVal, orig, {
4247 value: to_node(value[key], orig)
4250 return make_node(AST_Object, orig, {
4254 return make_node_from_constant(value, orig);
4257 function warn(node) {
4258 AST_Node.warn("global_defs {node} redefined [{file}:{line},{col}]", {
4260 file: node.start.file,
4261 line: node.start.line,
4262 col: node.start.col,
4266 AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
4267 if (!compressor.option("global_defs")) return this;
4268 this.figure_out_scope({ ie: compressor.option("ie") });
4269 return this.transform(new TreeTransformer(function(node) {
4270 var def = node._find_defs(compressor, "");
4272 var level = 0, child = node, parent;
4273 while (parent = this.parent(level++)) {
4274 if (!(parent instanceof AST_PropAccess)) break;
4275 if (parent.expression !== child) break;
4278 if (is_lhs(child, parent)) {
4285 def(AST_Node, noop);
4286 def(AST_Dot, function(compressor, suffix) {
4287 return this.expression._find_defs(compressor, "." + this.property + suffix);
4289 def(AST_SymbolDeclaration, function(compressor) {
4290 if (!this.definition().global) return;
4291 if (HOP(compressor.option("global_defs"), this.name)) warn(this);
4293 def(AST_SymbolRef, function(compressor, suffix) {
4294 if (!this.definition().global) return;
4295 var defines = compressor.option("global_defs");
4296 var name = this.name + suffix;
4297 if (HOP(defines, name)) return to_node(defines[name], this);
4299 })(function(node, func) {
4300 node.DEFMETHOD("_find_defs", func);
4303 function best_of_expression(ast1, ast2, threshold) {
4304 var delta = ast2.print_to_string().length - ast1.print_to_string().length;
4305 return delta < (threshold || 0) ? ast2 : ast1;
4308 function best_of_statement(ast1, ast2, threshold) {
4309 return best_of_expression(make_node(AST_SimpleStatement, ast1, {
4311 }), make_node(AST_SimpleStatement, ast2, {
4313 }), threshold).body;
4316 function best_of(compressor, ast1, ast2, threshold) {
4317 return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
4320 function convert_to_predicate(obj) {
4321 var map = Object.create(null);
4322 Object.keys(obj).forEach(function(key) {
4323 map[key] = makePredicate(obj[key]);
4328 function skip_directives(body) {
4329 for (var i = 0; i < body.length; i++) {
4331 if (!(stat instanceof AST_Directive)) return stat;
4335 function arrow_first_statement() {
4336 if (this.value) return make_node(AST_Return, this.value, {
4339 return skip_directives(this.body);
4341 AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement);
4342 AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement);
4343 AST_Lambda.DEFMETHOD("first_statement", function() {
4344 return skip_directives(this.body);
4347 AST_Lambda.DEFMETHOD("length", function() {
4348 var argnames = this.argnames;
4349 for (var i = 0; i < argnames.length; i++) {
4350 if (argnames[i] instanceof AST_DefaultValue) break;
4355 function try_evaluate(compressor, node) {
4356 var ev = node.evaluate(compressor);
4357 if (ev === node) return node;
4358 ev = make_node_from_constant(ev, node).optimize(compressor);
4359 return best_of(compressor, node, ev, compressor.eval_threshold);
4367 var native_fns = convert_to_predicate({
4373 ].concat(object_fns),
4374 Boolean: object_fns,
4375 Function: object_fns,
4380 ].concat(object_fns),
4385 ].concat(object_fns),
4403 ].concat(object_fns),
4405 var static_fns = convert_to_predicate({
4434 "getOwnPropertyDescriptor",
4435 "getOwnPropertyNames",
4448 function is_static_fn(node) {
4449 if (!(node instanceof AST_Dot)) return false;
4450 var expr = node.expression;
4451 if (!is_undeclared_ref(expr)) return false;
4452 var static_fn = static_fns[expr.name];
4453 return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random");
4456 // Accomodate when compress option evaluate=false
4457 // as well as the common constant expressions !0 and -1
4459 def(AST_Node, return_false);
4460 def(AST_Constant, return_true);
4461 def(AST_RegExp, return_false);
4462 var unaryPrefix = makePredicate("! ~ - + void");
4463 def(AST_UnaryPrefix, function() {
4464 return unaryPrefix[this.operator] && this.expression instanceof AST_Constant;
4466 })(function(node, func) {
4467 node.DEFMETHOD("is_constant", func);
4470 // methods to evaluate a constant expression
4472 // If the node has been successfully reduced to a constant,
4473 // then its value is returned; otherwise the element itself
4476 // They can be distinguished as constant value is never a
4477 // descendant of AST_Node.
4479 // When `ignore_side_effects` is `true`, inspect the constant value
4480 // produced without worrying about any side effects caused by said
4482 AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
4483 if (!compressor.option("evaluate")) return this;
4485 var val = this._eval(compressor, ignore_side_effects, cached, 1);
4486 cached.forEach(function(node) {
4489 if (ignore_side_effects) return val;
4490 if (!val || val instanceof RegExp) return val;
4491 if (typeof val == "function" || typeof val == "object") return this;
4494 var scan_modified = new TreeWalker(function(node) {
4495 if (node instanceof AST_Assign) modified(node.left);
4496 if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression);
4498 function modified(node) {
4499 if (node instanceof AST_DestructuredArray) {
4500 node.elements.forEach(modified);
4501 } else if (node instanceof AST_DestructuredObject) {
4502 node.properties.forEach(function(prop) {
4503 modified(prop.value);
4505 } else if (node instanceof AST_PropAccess) {
4506 modified(node.expression);
4507 } else if (node instanceof AST_SymbolRef) {
4508 node.definition().references.forEach(function(ref) {
4513 def(AST_Statement, function() {
4514 throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
4516 def(AST_Accessor, return_this);
4517 def(AST_BigInt, return_this);
4518 def(AST_Class, return_this);
4519 def(AST_Node, return_this);
4520 def(AST_Constant, function() {
4523 def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
4524 var lhs = this.left;
4525 if (!ignore_side_effects) {
4526 if (!(lhs instanceof AST_SymbolRef)) return this;
4527 if (!HOP(lhs, "_eval")) {
4528 if (!lhs.fixed) return this;
4529 var def = lhs.definition();
4530 if (!def.fixed) return this;
4531 if (def.undeclared) return this;
4532 if (def.last_ref !== lhs) return this;
4533 if (def.single_use == "m") return this;
4536 var op = this.operator;
4538 if (!HOP(lhs, "_eval") && lhs instanceof AST_SymbolRef && lhs.fixed && lhs.definition().fixed) {
4540 } else if (op == "=") {
4543 node = make_node(AST_Binary, this, {
4544 operator: op.slice(0, -1),
4549 lhs.walk(scan_modified);
4550 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4551 if (typeof value == "object") return this;
4555 def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
4556 if (!ignore_side_effects) return this;
4557 var exprs = this.expressions;
4558 for (var i = 0, last = exprs.length - 1; i < last; i++) {
4559 exprs[i].walk(scan_modified);
4561 var tail = exprs[last];
4562 var value = tail._eval(compressor, ignore_side_effects, cached, depth);
4563 return value === tail ? this : value;
4565 def(AST_Lambda, function(compressor) {
4566 if (compressor.option("unsafe")) {
4567 var fn = function() {};
4569 fn.toString = function() {
4570 return "function(){}";
4576 def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
4577 if (compressor.option("unsafe")) {
4579 for (var i = 0; i < this.elements.length; i++) {
4580 var element = this.elements[i];
4581 if (element instanceof AST_Hole) return this;
4582 var value = element._eval(compressor, ignore_side_effects, cached, depth);
4583 if (element === value) return this;
4584 elements.push(value);
4590 var nonsafe_props = makePredicate("__proto__ toString valueOf");
4591 def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
4592 if (compressor.option("unsafe")) {
4594 for (var i = 0; i < this.properties.length; i++) {
4595 var prop = this.properties[i];
4596 if (!(prop instanceof AST_ObjectKeyVal)) return this;
4598 if (key instanceof AST_Node) {
4599 key = key._eval(compressor, ignore_side_effects, cached, depth);
4600 if (key === prop.key) return this;
4602 if (nonsafe_props[key]) return this;
4603 val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
4604 if (val[key] === prop.value) return this;
4610 var non_converting_unary = makePredicate("! typeof void");
4611 def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
4612 var e = this.expression;
4613 var op = this.operator;
4614 // Function would be evaluated to an array and so typeof would
4615 // incorrectly return "object". Hence making is a special case.
4616 if (compressor.option("typeofs")
4618 && (e instanceof AST_Lambda
4619 || e instanceof AST_SymbolRef
4620 && e.fixed_value() instanceof AST_Lambda)) {
4621 return typeof function(){};
4623 var def = e instanceof AST_SymbolRef && e.definition();
4624 if (!non_converting_unary[op] && !(def && def.fixed)) depth++;
4625 e.walk(scan_modified);
4626 var v = e._eval(compressor, ignore_side_effects, cached, depth);
4628 if (ignore_side_effects && op == "void") return;
4632 case "!": return !v;
4634 // typeof <RegExp> returns "object" or "function" on different platforms
4635 // so cannot evaluate reliably
4636 if (v instanceof RegExp) return this;
4638 case "void": return;
4639 case "~": return ~v;
4640 case "-": return -v;
4641 case "+": return +v;
4644 if (!def) return this;
4645 if (!ignore_side_effects) {
4646 if (def.undeclared) return this;
4647 if (def.last_ref !== e) return this;
4649 if (HOP(e, "_eval")) v = +(op[0] + 1) + +v;
4655 def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) {
4656 var e = this.expression;
4657 if (!(e instanceof AST_SymbolRef)) {
4658 if (!ignore_side_effects) return this;
4659 } else if (!HOP(e, "_eval")) {
4660 if (!e.fixed) return this;
4661 if (!ignore_side_effects) {
4662 var def = e.definition();
4663 if (!def.fixed) return this;
4664 if (def.undeclared) return this;
4665 if (def.last_ref !== e) return this;
4668 if (!(e instanceof AST_SymbolRef && e.definition().fixed)) depth++;
4669 e.walk(scan_modified);
4670 var v = e._eval(compressor, ignore_side_effects, cached, depth);
4671 if (v === e) return this;
4675 var non_converting_binary = makePredicate("&& || === !==");
4676 def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
4677 if (!non_converting_binary[this.operator]) depth++;
4678 var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
4679 if (left === this.left) return this;
4680 if (this.operator == (left ? "||" : "&&")) return left;
4681 var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object");
4682 var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth);
4683 if (right === this.right) return this;
4685 switch (this.operator) {
4686 case "&&" : result = left && right; break;
4687 case "||" : result = left || right; break;
4689 result = left == null ? right : left;
4691 case "|" : result = left | right; break;
4692 case "&" : result = left & right; break;
4693 case "^" : result = left ^ right; break;
4694 case "+" : result = left + right; break;
4695 case "-" : result = left - right; break;
4696 case "*" : result = left * right; break;
4697 case "/" : result = left / right; break;
4698 case "%" : result = left % right; break;
4699 case "<<" : result = left << right; break;
4700 case ">>" : result = left >> right; break;
4701 case ">>>": result = left >>> right; break;
4702 case "==" : result = left == right; break;
4703 case "===": result = left === right; break;
4704 case "!=" : result = left != right; break;
4705 case "!==": result = left !== right; break;
4706 case "<" : result = left < right; break;
4707 case "<=" : result = left <= right; break;
4708 case ">" : result = left > right; break;
4709 case ">=" : result = left >= right; break;
4711 result = Math.pow(left, right);
4714 if (right && typeof right == "object" && HOP(right, left)) {
4721 if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
4722 if (compressor.option("unsafe_math")
4723 && !ignore_side_effects
4725 && typeof result == "number"
4726 && (this.operator == "+" || this.operator == "-")) {
4727 var digits = Math.max(0, decimals(left), decimals(right));
4728 // 53-bit significand ---> 15.95 decimal places
4729 if (digits < 16) return +result.toFixed(digits);
4733 function decimals(operand) {
4734 var match = /(\.[0-9]*)?(e.+)?$/.exec(+operand);
4735 return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
4738 def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
4739 var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
4740 if (condition === this.condition) return this;
4741 var node = condition ? this.consequent : this.alternative;
4742 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4743 return value === node ? this : value;
4745 function verify_escaped(ref, depth) {
4746 var escaped = ref.definition().escaped;
4747 switch (escaped.length) {
4752 escaped[0].walk(new TreeWalker(function(node) {
4753 if (found) return true;
4754 if (node === ref) return found = true;
4755 if (node instanceof AST_Scope) return true;
4759 return depth <= escaped.depth;
4762 def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
4763 var fixed = this.fixed_value();
4764 if (!fixed) return this;
4766 if (HOP(fixed, "_eval")) {
4767 value = fixed._eval();
4769 this._eval = return_this;
4770 value = fixed._eval(compressor, ignore_side_effects, cached, depth);
4772 if (value === fixed) return this;
4773 fixed._eval = function() {
4778 return value && typeof value == "object" && !verify_escaped(this, depth) ? this : value;
4787 var static_values = convert_to_predicate({
4802 "NEGATIVE_INFINITY",
4803 "POSITIVE_INFINITY",
4806 var regexp_props = makePredicate("global ignoreCase multiline source");
4807 def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
4808 if (compressor.option("unsafe")) {
4810 var exp = this.expression;
4811 if (!is_undeclared_ref(exp)) {
4812 val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
4813 if (val == null || val === exp) return this;
4815 var key = this.property;
4816 if (key instanceof AST_Node) {
4817 key = key._eval(compressor, ignore_side_effects, cached, depth);
4818 if (key === this.property) return this;
4820 if (val === undefined) {
4821 var static_value = static_values[exp.name];
4822 if (!static_value || !static_value[key]) return this;
4823 val = global_objs[exp.name];
4824 } else if (val instanceof RegExp) {
4825 if (!regexp_props[key]) return this;
4826 } else if (typeof val == "object") {
4827 if (!HOP(val, key)) return this;
4828 } else if (typeof val == "function") switch (key) {
4830 return val.node.name ? val.node.name.name : "";
4832 return val.node.length();
4840 function eval_all(nodes, compressor, ignore_side_effects, cached, depth) {
4842 for (var i = 0; i < nodes.length; i++) {
4843 var node = nodes[i];
4844 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4845 if (node === value) return;
4850 def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
4851 var exp = this.expression;
4852 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
4853 if (fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function) {
4854 if (fn.evaluating) return this;
4855 if (fn.name && fn.name.definition().recursive_refs > 0) return this;
4856 if (this.is_expr_pure(compressor)) return this;
4857 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4858 if (!all(fn.argnames, function(sym, index) {
4859 if (sym instanceof AST_DefaultValue) {
4860 if (!args) return false;
4861 if (args[index] === undefined) {
4862 var value = sym.value._eval(compressor, ignore_side_effects, cached, depth);
4863 if (value === sym.value) return false;
4864 args[index] = value;
4868 return !(sym instanceof AST_Destructured);
4870 if (fn.rest instanceof AST_Destructured) return this;
4871 if (!args && !ignore_side_effects) return this;
4872 var stat = fn.first_statement();
4873 if (!(stat instanceof AST_Return)) {
4874 if (ignore_side_effects) {
4875 fn.walk(scan_modified);
4877 fn.evaluating = true;
4878 walk_body(fn, new TreeWalker(function(node) {
4879 if (found) return true;
4880 if (node instanceof AST_Return) {
4881 if (node.value && node.value._eval(compressor, true, cached, depth) !== undefined) {
4886 if (node instanceof AST_Scope && node !== fn) return true;
4888 delete fn.evaluating;
4893 var val = stat.value;
4895 var cached_args = [];
4896 if (!args || all(fn.argnames, function(sym, i) {
4897 return assign(sym, args[i]);
4898 }) && !(fn.rest && !assign(fn.rest, args.slice(fn.argnames.length))) || ignore_side_effects) {
4899 if (ignore_side_effects) fn.argnames.forEach(function(sym) {
4900 if (sym instanceof AST_DefaultValue) sym.value.walk(scan_modified);
4902 fn.evaluating = true;
4903 val = val._eval(compressor, ignore_side_effects, cached, depth);
4904 delete fn.evaluating;
4906 cached_args.forEach(function(node) {
4909 return val === stat.value ? this : val;
4910 } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
4911 var key = exp.property;
4912 if (key instanceof AST_Node) {
4913 key = key._eval(compressor, ignore_side_effects, cached, depth);
4914 if (key === exp.property) return this;
4917 var e = exp.expression;
4918 if (is_undeclared_ref(e)) {
4919 var static_fn = static_fns[e.name];
4920 if (!static_fn || !static_fn[key]) return this;
4921 val = global_objs[e.name];
4923 val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
4924 if (val == null || val === e) return this;
4925 var native_fn = native_fns[val.constructor.name];
4926 if (!native_fn || !native_fn[key]) return this;
4927 if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
4929 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4930 if (!args) return this;
4931 if (key == "replace" && typeof args[1] == "function") return this;
4933 return val[key].apply(val, args);
4935 AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
4937 file: this.start.file,
4938 line: this.start.line,
4939 col: this.start.col,
4942 if (val instanceof RegExp) val.lastIndex = 0;
4947 function assign(sym, arg) {
4948 if (sym instanceof AST_DefaultValue) sym = sym.name;
4949 var def = sym.definition();
4950 if (def.orig[def.orig.length - 1] !== sym) return false;
4952 def.references.forEach(function(node) {
4953 node._eval = function() {
4956 cached_args.push(node);
4961 def(AST_New, return_this);
4962 def(AST_Template, function(compressor, ignore_side_effects, cached, depth) {
4963 if (!compressor.option("templates")) return this;
4965 if (!is_raw_tag(compressor, this.tag)) return this;
4966 decode = function(str) {
4970 var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth);
4971 if (!exprs) return this;
4972 var malformed = false;
4973 var ret = decode(this.strings[0]);
4974 for (var i = 0; i < exprs.length; i++) {
4975 ret += exprs[i] + decode(this.strings[i + 1]);
4977 if (!malformed) return ret;
4978 this._eval = return_this;
4981 function decode(str) {
4982 str = decode_template(str);
4983 if (typeof str != "string") malformed = true;
4987 })(function(node, func) {
4988 node.DEFMETHOD("_eval", func);
4991 // method to negate an expression
4993 function basic_negation(exp) {
4994 return make_node(AST_UnaryPrefix, exp, {
4999 function best(orig, alt, first_in_statement) {
5000 var negated = basic_negation(orig);
5001 if (first_in_statement) {
5002 var stat = make_node(AST_SimpleStatement, alt, {
5005 return best_of_expression(negated, stat) === stat ? alt : negated;
5007 return best_of_expression(negated, alt);
5009 def(AST_Node, function() {
5010 return basic_negation(this);
5012 def(AST_Statement, function() {
5013 throw new Error("Cannot negate a statement");
5015 def(AST_Binary, function(compressor, first_in_statement) {
5016 var self = this.clone(), op = this.operator;
5017 if (compressor.option("unsafe_comps")) {
5019 case "<=" : self.operator = ">" ; return self;
5020 case "<" : self.operator = ">=" ; return self;
5021 case ">=" : self.operator = "<" ; return self;
5022 case ">" : self.operator = "<=" ; return self;
5026 case "==" : self.operator = "!="; return self;
5027 case "!=" : self.operator = "=="; return self;
5028 case "===": self.operator = "!=="; return self;
5029 case "!==": self.operator = "==="; return self;
5031 self.operator = "||";
5032 self.left = self.left.negate(compressor, first_in_statement);
5033 self.right = self.right.negate(compressor);
5034 return best(this, self, first_in_statement);
5036 self.operator = "&&";
5037 self.left = self.left.negate(compressor, first_in_statement);
5038 self.right = self.right.negate(compressor);
5039 return best(this, self, first_in_statement);
5041 return basic_negation(this);
5043 def(AST_ClassExpression, function() {
5044 return basic_negation(this);
5046 def(AST_Conditional, function(compressor, first_in_statement) {
5047 var self = this.clone();
5048 self.consequent = self.consequent.negate(compressor);
5049 self.alternative = self.alternative.negate(compressor);
5050 return best(this, self, first_in_statement);
5052 def(AST_LambdaExpression, function() {
5053 return basic_negation(this);
5055 def(AST_Sequence, function(compressor) {
5056 var expressions = this.expressions.slice();
5057 expressions.push(expressions.pop().negate(compressor));
5058 return make_sequence(this, expressions);
5060 def(AST_UnaryPrefix, function() {
5061 if (this.operator == "!")
5062 return this.expression;
5063 return basic_negation(this);
5065 })(function(node, func) {
5066 node.DEFMETHOD("negate", function(compressor, first_in_statement) {
5067 return func.call(this, compressor, first_in_statement);
5071 var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
5072 var global_pure_constructors = makePredicate("Map Set WeakMap WeakSet");
5073 AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
5074 if (compressor.option("unsafe")) {
5075 var expr = this.expression;
5076 if (is_undeclared_ref(expr)) {
5077 if (global_pure_fns[expr.name]) return true;
5078 if (this instanceof AST_New && global_pure_constructors[expr.name]) return true;
5080 if (is_static_fn(expr)) return true;
5082 return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this);
5084 AST_Template.DEFMETHOD("is_expr_pure", function(compressor) {
5086 if (!tag) return true;
5087 if (compressor.option("unsafe")) {
5088 if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true;
5089 if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) {
5090 var static_fn = static_fns[tag.expression.name];
5091 return static_fn && (static_fn[tag.property]
5092 || tag.expression.name == "Math" && tag.property == "random");
5095 return !compressor.pure_funcs(this);
5097 AST_Node.DEFMETHOD("is_call_pure", return_false);
5098 AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
5099 if (!compressor.option("unsafe")) return false;
5100 var dot = this.expression;
5101 if (!(dot instanceof AST_Dot)) return false;
5102 var exp = dot.expression;
5104 var prop = dot.property;
5105 if (exp instanceof AST_Array) {
5106 map = native_fns.Array;
5107 } else if (exp.is_boolean(compressor)) {
5108 map = native_fns.Boolean;
5109 } else if (exp.is_number(compressor)) {
5110 map = native_fns.Number;
5111 } else if (exp instanceof AST_RegExp) {
5112 map = native_fns.RegExp;
5113 } else if (exp.is_string(compressor)) {
5114 map = native_fns.String;
5115 if (prop == "replace") {
5116 var arg = this.args[1];
5117 if (arg && !arg.is_string(compressor)) return false;
5119 } else if (!dot.may_throw_on_access(compressor)) {
5120 map = native_fns.Object;
5122 return map && map[prop];
5125 function spread_side_effects(exp) {
5126 while ((exp = exp.tail_node()) instanceof AST_SymbolRef) {
5127 exp = exp.fixed_value();
5128 if (!exp) return true;
5130 return !(exp instanceof AST_Array
5131 || exp.TYPE == "Binary" && !lazy_op[exp.operator]
5132 || exp instanceof AST_Constant
5133 || exp instanceof AST_Lambda
5134 || exp instanceof AST_Object && all(exp.properties, function(prop) {
5135 return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
5137 || exp instanceof AST_ObjectIdentity
5138 || exp instanceof AST_Unary);
5141 // determine if expression has side effects
5143 function any(list, compressor, spread) {
5144 return !all(list, spread ? function(node) {
5145 return node instanceof AST_Spread ? !spread(node, compressor) : !node.has_side_effects(compressor);
5146 } : function(node) {
5147 return !node.has_side_effects(compressor);
5150 function array_spread(node, compressor) {
5151 return !node.expression.is_string(compressor) || node.expression.has_side_effects(compressor);
5153 def(AST_Node, return_true);
5154 def(AST_Array, function(compressor) {
5155 return any(this.elements, compressor, array_spread);
5157 def(AST_Assign, function(compressor) {
5158 var lhs = this.left;
5159 if (!(lhs instanceof AST_PropAccess)) return true;
5160 var node = lhs.expression;
5161 return !(node instanceof AST_ObjectIdentity)
5162 || !node.scope.resolve().new
5163 || lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
5164 || this.right.has_side_effects(compressor);
5166 def(AST_Binary, function(compressor) {
5167 return this.left.has_side_effects(compressor)
5168 || this.right.has_side_effects(compressor)
5169 || this.operator == "in" && !is_object(this.right);
5171 def(AST_Block, function(compressor) {
5172 return any(this.body, compressor);
5174 def(AST_Call, function(compressor) {
5175 if (!this.is_expr_pure(compressor)
5176 && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) {
5179 return any(this.args, compressor, array_spread);
5181 def(AST_Case, function(compressor) {
5182 return this.expression.has_side_effects(compressor)
5183 || any(this.body, compressor);
5185 def(AST_Class, function(compressor) {
5186 var base = this.extends;
5188 if (base instanceof AST_SymbolRef) base = base.fixed_value();
5189 if (!safe_for_extends(base)) return true;
5191 return any(this.properties, compressor);
5193 def(AST_ClassProperty, function(compressor) {
5194 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5195 || this.static && this.value && this.value.has_side_effects(compressor);
5197 def(AST_Conditional, function(compressor) {
5198 return this.condition.has_side_effects(compressor)
5199 || this.consequent.has_side_effects(compressor)
5200 || this.alternative.has_side_effects(compressor);
5202 def(AST_Constant, return_false);
5203 def(AST_Definitions, function(compressor) {
5204 return any(this.definitions, compressor);
5206 def(AST_DestructuredArray, function(compressor) {
5207 return any(this.elements, compressor);
5209 def(AST_DestructuredKeyVal, function(compressor) {
5210 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5211 || this.value.has_side_effects(compressor);
5213 def(AST_DestructuredObject, function(compressor) {
5214 return any(this.properties, compressor);
5216 def(AST_Dot, function(compressor) {
5217 return !this.optional && this.expression.may_throw_on_access(compressor)
5218 || this.expression.has_side_effects(compressor);
5220 def(AST_EmptyStatement, return_false);
5221 def(AST_If, function(compressor) {
5222 return this.condition.has_side_effects(compressor)
5223 || this.body && this.body.has_side_effects(compressor)
5224 || this.alternative && this.alternative.has_side_effects(compressor);
5226 def(AST_LabeledStatement, function(compressor) {
5227 return this.body.has_side_effects(compressor);
5229 def(AST_Lambda, return_false);
5230 def(AST_Object, function(compressor) {
5231 return any(this.properties, compressor, function(node, compressor) {
5232 var exp = node.expression;
5233 return spread_side_effects(exp) || exp.has_side_effects(compressor);
5236 def(AST_ObjectIdentity, return_false);
5237 def(AST_ObjectProperty, function(compressor) {
5238 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5239 || this.value.has_side_effects(compressor);
5241 def(AST_Sequence, function(compressor) {
5242 return any(this.expressions, compressor);
5244 def(AST_SimpleStatement, function(compressor) {
5245 return this.body.has_side_effects(compressor);
5247 def(AST_Sub, function(compressor) {
5248 return !this.optional && this.expression.may_throw_on_access(compressor)
5249 || this.expression.has_side_effects(compressor)
5250 || this.property.has_side_effects(compressor);
5252 def(AST_Switch, function(compressor) {
5253 return this.expression.has_side_effects(compressor)
5254 || any(this.body, compressor);
5256 def(AST_SymbolDeclaration, return_false);
5257 def(AST_SymbolRef, function(compressor) {
5258 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5260 def(AST_Template, function(compressor) {
5261 return !this.is_expr_pure(compressor) || any(this.expressions, compressor);
5263 def(AST_Try, function(compressor) {
5264 return any(this.body, compressor)
5265 || this.bcatch && this.bcatch.has_side_effects(compressor)
5266 || this.bfinally && this.bfinally.has_side_effects(compressor);
5268 def(AST_Unary, function(compressor) {
5269 return unary_side_effects[this.operator]
5270 || this.expression.has_side_effects(compressor);
5272 def(AST_VarDef, function() {
5275 })(function(node, func) {
5276 node.DEFMETHOD("has_side_effects", func);
5279 // determine if expression may throw
5281 def(AST_Node, return_true);
5283 def(AST_Constant, return_false);
5284 def(AST_Destructured, return_true);
5285 def(AST_EmptyStatement, return_false);
5286 def(AST_Lambda, return_false);
5287 def(AST_ObjectIdentity, return_false);
5288 def(AST_SymbolDeclaration, return_false);
5290 function any(list, compressor) {
5291 for (var i = list.length; --i >= 0;)
5292 if (list[i].may_throw(compressor))
5297 function call_may_throw(exp, compressor) {
5298 if (exp.may_throw(compressor)) return true;
5299 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
5300 if (!(exp instanceof AST_Lambda)) return true;
5301 if (any(exp.argnames, compressor)) return true;
5302 if (any(exp.body, compressor)) return true;
5303 return is_arrow(exp) && exp.value && exp.value.may_throw(compressor);
5306 def(AST_Array, function(compressor) {
5307 return any(this.elements, compressor);
5309 def(AST_Assign, function(compressor) {
5310 if (this.right.may_throw(compressor)) return true;
5311 if (!compressor.has_directive("use strict")
5312 && this.operator == "="
5313 && this.left instanceof AST_SymbolRef) {
5316 return this.left.may_throw(compressor);
5318 def(AST_Binary, function(compressor) {
5319 return this.left.may_throw(compressor)
5320 || this.right.may_throw(compressor)
5321 || this.operator == "in" && !is_object(this.right);
5323 def(AST_Block, function(compressor) {
5324 return any(this.body, compressor);
5326 def(AST_Call, function(compressor) {
5327 if (any(this.args, compressor)) return true;
5328 if (this.is_expr_pure(compressor)) return false;
5329 this.may_throw = return_true;
5330 var ret = call_may_throw(this.expression, compressor);
5331 delete this.may_throw;
5334 def(AST_Case, function(compressor) {
5335 return this.expression.may_throw(compressor)
5336 || any(this.body, compressor);
5338 def(AST_Conditional, function(compressor) {
5339 return this.condition.may_throw(compressor)
5340 || this.consequent.may_throw(compressor)
5341 || this.alternative.may_throw(compressor);
5343 def(AST_DefaultValue, function(compressor) {
5344 return this.name.may_throw(compressor)
5345 || this.value && this.value.may_throw(compressor);
5347 def(AST_Definitions, function(compressor) {
5348 return any(this.definitions, compressor);
5350 def(AST_Dot, function(compressor) {
5351 return !this.optional && this.expression.may_throw_on_access(compressor)
5352 || this.expression.may_throw(compressor);
5354 def(AST_If, function(compressor) {
5355 return this.condition.may_throw(compressor)
5356 || this.body && this.body.may_throw(compressor)
5357 || this.alternative && this.alternative.may_throw(compressor);
5359 def(AST_LabeledStatement, function(compressor) {
5360 return this.body.may_throw(compressor);
5362 def(AST_Object, function(compressor) {
5363 return any(this.properties, compressor);
5365 def(AST_ObjectProperty, function(compressor) {
5366 return this.value.may_throw(compressor)
5367 || this.key instanceof AST_Node && this.key.may_throw(compressor);
5369 def(AST_Return, function(compressor) {
5370 return this.value && this.value.may_throw(compressor);
5372 def(AST_Sequence, function(compressor) {
5373 return any(this.expressions, compressor);
5375 def(AST_SimpleStatement, function(compressor) {
5376 return this.body.may_throw(compressor);
5378 def(AST_Sub, function(compressor) {
5379 return !this.optional && this.expression.may_throw_on_access(compressor)
5380 || this.expression.may_throw(compressor)
5381 || this.property.may_throw(compressor);
5383 def(AST_Switch, function(compressor) {
5384 return this.expression.may_throw(compressor)
5385 || any(this.body, compressor);
5387 def(AST_SymbolRef, function(compressor) {
5388 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5390 def(AST_Template, function(compressor) {
5391 if (any(this.expressions, compressor)) return true;
5392 if (this.is_expr_pure(compressor)) return false;
5393 if (!this.tag) return false;
5394 this.may_throw = return_true;
5395 var ret = call_may_throw(this.tag, compressor);
5396 delete this.may_throw;
5399 def(AST_Try, function(compressor) {
5400 return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor))
5401 || this.bfinally && this.bfinally.may_throw(compressor);
5403 def(AST_Unary, function(compressor) {
5404 return this.expression.may_throw(compressor)
5405 && !(this.operator == "typeof" && this.expression instanceof AST_SymbolRef);
5407 def(AST_VarDef, function(compressor) {
5408 return this.name.may_throw(compressor)
5409 || this.value && this.value.may_throw(compressor);
5411 })(function(node, func) {
5412 node.DEFMETHOD("may_throw", func);
5415 // determine if expression is constant
5417 function all_constant(list, scope) {
5418 for (var i = list.length; --i >= 0;)
5419 if (!list[i].is_constant_expression(scope))
5423 def(AST_Node, return_false);
5424 def(AST_Array, function(scope) {
5425 return all_constant(this.elements, scope);
5427 def(AST_Binary, function(scope) {
5428 return this.left.is_constant_expression(scope)
5429 && this.right.is_constant_expression(scope)
5430 && (this.operator != "in" || is_object(this.right));
5432 def(AST_Class, function(scope) {
5433 var base = this.extends;
5434 if (base && !safe_for_extends(base)) return false;
5435 return all_constant(this.properties, scope);
5437 def(AST_ClassProperty, function(scope) {
5438 return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope));
5440 def(AST_Constant, return_true);
5441 def(AST_Lambda, function(scope) {
5445 self.walk(new TreeWalker(function(node, descend) {
5446 if (!result) return true;
5447 if (node instanceof AST_BlockScope) {
5448 if (node === self) return;
5454 if (node instanceof AST_SymbolRef) {
5455 if (self.inlined || node.redef) {
5459 if (self.variables.has(node.name)) return true;
5460 var def = node.definition();
5461 if (member(def.scope, scopes)) return true;
5462 if (scope && !def.redefined()) {
5463 var scope_def = scope.find_variable(node.name);
5464 if (scope_def ? scope_def === def : def.undeclared) {
5472 if (node instanceof AST_ObjectIdentity) {
5473 if (is_arrow(self) && all(scopes, function(s) {
5474 return !(s instanceof AST_Scope) || is_arrow(s);
5481 def(AST_Object, function(scope) {
5482 return all_constant(this.properties, scope);
5484 def(AST_ObjectProperty, function(scope) {
5485 return typeof this.key == "string" && this.value.is_constant_expression(scope);
5487 def(AST_Unary, function(scope) {
5488 return this.expression.is_constant_expression(scope);
5490 })(function(node, func) {
5491 node.DEFMETHOD("is_constant_expression", func);
5494 // tell me if a statement aborts
5495 function aborts(thing) {
5496 return thing && thing.aborts();
5499 def(AST_Statement, return_null);
5500 def(AST_Jump, return_this);
5501 function block_aborts() {
5502 var n = this.body.length;
5503 return n > 0 && aborts(this.body[n - 1]);
5505 def(AST_BlockStatement, block_aborts);
5506 def(AST_SwitchBranch, block_aborts);
5507 def(AST_If, function() {
5508 return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
5510 })(function(node, func) {
5511 node.DEFMETHOD("aborts", func);
5514 /* -----[ optimizers ]----- */
5516 var directives = makePredicate(["use asm", "use strict"]);
5517 OPT(AST_Directive, function(self, compressor) {
5518 if (compressor.option("directives")
5519 && (!directives[self.value] || compressor.has_directive(self.value) !== self)) {
5520 return make_node(AST_EmptyStatement, self);
5525 OPT(AST_Debugger, function(self, compressor) {
5526 if (compressor.option("drop_debugger"))
5527 return make_node(AST_EmptyStatement, self);
5531 OPT(AST_LabeledStatement, function(self, compressor) {
5532 if (compressor.option("dead_code")
5533 && self.body instanceof AST_Break
5534 && compressor.loopcontrol_target(self.body) === self.body) {
5535 return make_node(AST_EmptyStatement, self);
5537 return compressor.option("unused") && self.label.references.length == 0 ? self.body : self;
5540 OPT(AST_LoopControl, function(self, compressor) {
5541 if (!compressor.option("dead_code")) return self;
5542 var label = self.label;
5544 var lct = compressor.loopcontrol_target(self);
5546 if (compressor.loopcontrol_target(self) === lct) {
5547 remove(label.thedef.references, self);
5555 OPT(AST_Block, function(self, compressor) {
5556 self.body = tighten_body(self.body, compressor);
5560 function trim_block(node, parent, in_list) {
5561 switch (node.body.length) {
5563 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
5565 var stat = node.body[0];
5566 if (!safe_to_trim(stat)) return node;
5567 if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node;
5573 OPT(AST_BlockStatement, function(self, compressor) {
5574 self.body = tighten_body(self.body, compressor);
5575 return trim_block(self, compressor.parent());
5578 function drop_rest_farg(fn, compressor) {
5579 if (!compressor.option("rests")) return;
5580 if (fn.uses_arguments) return;
5581 if (!(fn.rest instanceof AST_DestructuredArray)) return;
5582 if (!compressor.drop_fargs(fn, compressor.parent())) return;
5583 fn.argnames = fn.argnames.concat(fn.rest.elements);
5584 fn.rest = fn.rest.rest;
5587 OPT(AST_Lambda, function(self, compressor) {
5588 drop_rest_farg(self, compressor);
5589 self.body = tighten_body(self.body, compressor);
5593 function opt_arrow(self, compressor) {
5594 if (!compressor.option("arrows")) return self;
5595 drop_rest_farg(self, compressor);
5596 var body = tighten_body(self.value ? [ self.first_statement() ] : self.body, compressor);
5597 switch (body.length) {
5600 if (stat instanceof AST_Return) {
5601 self.body.length = 0;
5602 self.value = stat.value;
5612 OPT(AST_Arrow, opt_arrow);
5613 OPT(AST_AsyncArrow, opt_arrow);
5615 OPT(AST_Function, function(self, compressor) {
5616 drop_rest_farg(self, compressor);
5617 self.body = tighten_body(self.body, compressor);
5618 var parent = compressor.parent();
5619 if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
5620 var stat = self.body[i];
5621 if (stat instanceof AST_Directive) continue;
5622 if (stat instanceof AST_Return) {
5623 if (i != self.body.length - 1) break;
5624 var call = stat.value;
5625 if (!call || call.TYPE != "Call") break;
5626 if (call.is_expr_pure(compressor)) break;
5627 var fn = call.expression;
5628 if (fn instanceof AST_SymbolRef) {
5629 if (self.name && self.name.definition() === fn.definition()) break;
5630 fn = fn.fixed_value();
5632 if (!(fn instanceof AST_Defun || fn instanceof AST_Function)) break;
5634 if (fn.uses_arguments) break;
5635 if (fn === call.expression) {
5636 if (fn.parent_scope !== self) break;
5637 if (!all(fn.enclosed, function(def) {
5638 return def.scope !== self;
5642 && (parent instanceof AST_ClassMethod || parent instanceof AST_ObjectMethod)
5643 && parent.value === compressor.self()) break;
5644 if (fn.contains_this()) break;
5645 var len = fn.argnames.length;
5646 if (len > 0 && compressor.option("inline") < 2) break;
5647 if (len > self.argnames.length) break;
5648 if (!all(self.argnames, function(argname) {
5649 return argname instanceof AST_SymbolFunarg;
5651 if (!all(call.args, function(arg) {
5652 return !(arg instanceof AST_Spread);
5654 for (var j = 0; j < len; j++) {
5655 var arg = call.args[j];
5656 if (!(arg instanceof AST_SymbolRef)) break;
5657 if (arg.definition() !== self.argnames[j].definition()) break;
5660 for (; j < call.args.length; j++) {
5661 if (call.args[j].has_side_effects(compressor)) break;
5663 if (j < call.args.length) break;
5664 if (len < self.argnames.length && !compressor.drop_fargs(self, parent)) {
5665 if (!compressor.drop_fargs(fn, call)) break;
5667 fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len));
5668 } while (++len < self.argnames.length);
5670 return call.expression;
5677 var NO_MERGE = makePredicate("arguments await yield");
5678 AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
5679 if (!compressor.option("merge_vars")) return;
5680 var in_try, root, segment = {}, self = this;
5681 var first = [], last = [], index = 0;
5682 var declarations = new Dictionary();
5683 var references = Object.create(null);
5684 var prev = Object.create(null);
5685 var tw = new TreeWalker(function(node, descend) {
5686 if (node instanceof AST_Assign) {
5687 var lhs = node.left;
5688 var rhs = node.right;
5689 if (lhs instanceof AST_Destructured) {
5691 var marker = new TreeWalker(function(node) {
5692 if (node instanceof AST_Destructured) return;
5693 if (node instanceof AST_DefaultValue) {
5695 node.value.walk(tw);
5697 node.name.walk(marker);
5698 } else if (node instanceof AST_DestructuredKeyVal) {
5699 if (node.key instanceof AST_Node) {
5701 segment.block = node;
5703 node.value.walk(marker);
5706 node.value.walk(marker);
5708 } else if (node instanceof AST_SymbolRef) {
5718 if (lazy_op[node.operator.slice(0, -1)]) {
5722 if (lhs instanceof AST_SymbolRef) mark(lhs);
5726 if (lhs instanceof AST_SymbolRef) {
5727 if (node.operator != "=") mark(lhs, true);
5734 if (node instanceof AST_Binary) {
5735 if (!lazy_op[node.operator]) return;
5738 node.right.walk(tw);
5742 if (node instanceof AST_Break) {
5743 var target = tw.loopcontrol_target(node);
5744 if (!(target instanceof AST_IterationStatement)) insert(target);
5747 if (node instanceof AST_Call) {
5748 var exp = node.expression;
5749 var tail = exp.tail_node();
5750 if (!is_lambda(tail)) {
5752 return mark_expression(exp);
5754 if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
5757 node.args.forEach(function(arg) {
5763 if (node instanceof AST_Conditional) {
5764 node.condition.walk(tw);
5766 node.consequent.walk(tw);
5769 node.alternative.walk(tw);
5773 if (node instanceof AST_Continue) {
5774 var target = tw.loopcontrol_target(node);
5775 if (target instanceof AST_Do) insert(target);
5778 if (node instanceof AST_Do) {
5780 segment.block = node;
5781 segment.loop = true;
5784 if (segment.inserted === node) segment = save;
5785 node.condition.walk(tw);
5789 if (node instanceof AST_For) {
5790 if (node.init) node.init.walk(tw);
5792 segment.block = node;
5793 segment.loop = true;
5794 if (node.condition) node.condition.walk(tw);
5796 if (node.step) node.step.walk(tw);
5800 if (node instanceof AST_ForEnumeration) {
5801 node.object.walk(tw);
5803 segment.block = node;
5804 segment.loop = true;
5810 if (node instanceof AST_If) {
5811 node.condition.walk(tw);
5815 if (node.alternative) {
5817 node.alternative.walk(tw);
5822 if (node instanceof AST_LabeledStatement) {
5824 segment.block = node;
5827 if (segment.inserted === node) segment = save;
5831 if (node instanceof AST_Scope) {
5833 segment.block = node;
5834 if (node === self) root = segment;
5835 if (node instanceof AST_Lambda) {
5836 if (node.name) references[node.name.definition().id] = false;
5837 var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) {
5838 if (node instanceof AST_SymbolFunarg) references[node.definition().id] = false;
5839 } : function(node) {
5840 if (node instanceof AST_SymbolFunarg) mark(node);
5842 var scanner = new TreeWalker(function(ref) {
5843 if (ref instanceof AST_SymbolDeclaration) references[ref.definition().id] = false;
5844 if (!(ref instanceof AST_SymbolRef)) return;
5845 var def = ref.definition();
5846 var ldef = node.variables.get(ref.name);
5847 if (ldef && (ldef === def
5849 || node.parent_scope.find_variable(ref.name) === def)) {
5850 references[def.id] = false;
5851 references[ldef.id] = false;
5860 node.argnames.forEach(function(argname) {
5861 argname.mark_symbol(marker, scanner);
5863 if (node.rest) node.rest.mark_symbol(marker, scanner);
5865 walk_lambda(node, tw);
5869 if (node instanceof AST_Sub) {
5870 var exp = node.expression;
5871 if (node.optional) {
5874 node.property.walk(tw);
5879 return mark_expression(exp);
5881 if (node instanceof AST_Switch) {
5882 node.expression.walk(tw);
5884 node.body.forEach(function(branch) {
5885 if (branch instanceof AST_Default) return;
5886 branch.expression.walk(tw);
5887 if (save === segment) push();
5890 node.body.forEach(function(branch) {
5892 segment.block = node;
5894 walk_body(branch, tw);
5895 if (segment.inserted === node) segment = save;
5900 if (node instanceof AST_SymbolConst || node instanceof AST_SymbolLet) {
5901 references[node.definition().id] = false;
5904 if (node instanceof AST_SymbolRef) {
5908 if (node instanceof AST_Try) {
5909 var save_try = in_try;
5912 walk_body(node, tw);
5915 if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
5916 if (node instanceof AST_SymbolCatch) {
5917 var def = node.definition();
5918 references[def.id] = false;
5919 if (def = def.redefined()) references[def.id] = false;
5922 if (node.bfinally || (in_try = save_try)) {
5923 walk_body(node.bcatch, tw);
5926 walk_body(node.bcatch, tw);
5932 if (node.bfinally) node.bfinally.walk(tw);
5935 if (node instanceof AST_Unary) {
5936 if (!UNARY_POSTFIX[node.operator]) return;
5937 var sym = node.expression;
5938 if (!(sym instanceof AST_SymbolRef)) return;
5942 if (node instanceof AST_VarDef) {
5943 var assigned = node.value;
5947 assigned = segment.block instanceof AST_ForEnumeration && segment.block.init === tw.parent();
5949 node.name.mark_symbol(assigned ? function(node) {
5950 if (!(node instanceof AST_SymbolDeclaration)) return;
5951 if (node instanceof AST_SymbolVar) {
5954 references[node.definition().id] = false;
5957 } : function(node) {
5958 if (!(node instanceof AST_SymbolDeclaration)) return;
5959 var id = node.definition().id;
5960 if (!(node instanceof AST_SymbolVar)) {
5961 references[id] = false;
5962 } else if (!(id in references)) {
5963 declarations.add(id, node);
5964 } else if (references[id]) {
5965 references[id].push(node);
5971 if (node instanceof AST_While) {
5973 segment.block = node;
5974 segment.loop = true;
5980 function mark_expression(exp) {
5981 if (compressor.option("ie")) {
5982 var sym = root_expr(exp);
5983 if (sym instanceof AST_SymbolRef) sym.walk(tw);
5988 tw.directives = Object.create(compressor.directives);
5990 var merged = Object.create(null);
5991 while (first.length && last.length) {
5992 var head = first.pop();
5993 var def = head.definition;
5994 if (!(def.id in prev)) continue;
5995 if (!references[def.id]) continue;
5997 start: references[def.id].start,
5999 while (def.id in merged) def = merged[def.id];
6000 head_refs.end = references[def.id].end;
6003 var tail = last.pop();
6004 if (!tail) continue;
6005 if (tail.index > head.index) continue;
6006 var id = tail.definition.id;
6007 var tail_refs = references[id];
6008 if (!tail_refs) continue;
6009 if (head_refs.start.block !== tail_refs.start.block
6010 || !mergeable(head_refs, tail_refs)
6011 || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs)
6012 || compressor.option("webkit") && is_funarg(def) !== is_funarg(tail.definition)
6013 || !all(tail_refs, function(sym) {
6014 return sym.scope.find_variable(def.name) === def;
6016 skipped.unshift(tail);
6019 var orig = [], refs = [];
6020 tail_refs.forEach(function(sym) {
6022 sym.name = def.name;
6023 if (sym instanceof AST_SymbolRef) {
6029 def.orig = orig.concat(def.orig);
6030 def.references = refs.concat(def.references);
6031 def.fixed = tail.definition.fixed && def.fixed;
6034 } while (last.length);
6035 if (skipped.length) last = last.concat(skipped);
6039 segment = Object.create(segment);
6043 segment = Object.getPrototypeOf(segment);
6046 function mark(sym, read) {
6047 var def = sym.definition(), ldef, seg = segment;
6053 if (def.id in references) {
6054 var refs = references[def.id];
6056 if (refs.start.block !== seg.block) return references[def.id] = false;
6059 if (def.id in prev) {
6060 last[prev[def.id]] = null;
6064 } else if ((ldef = self.variables.get(def.name)) !== def) {
6065 if (ldef && root === seg) references[ldef.id] = false;
6066 return references[def.id] = false;
6067 } else if (compressor.exposed(def) || NO_MERGE[sym.name]) {
6068 return references[def.id] = false;
6070 var refs = declarations.get(def.id) || [];
6072 references[def.id] = refs;
6080 if (seg.block !== self) return references[def.id] = false;
6083 prev[def.id] = last.length;
6090 function insert(target) {
6093 if (HOP(segment, "block")) {
6094 var block = segment.block;
6095 if (block instanceof AST_LabeledStatement) block = block.body;
6096 if (block === target) break;
6098 stack.push(segment);
6101 segment.inserted = segment.block;
6103 while (stack.length) {
6104 var seg = stack.pop();
6106 if (HOP(seg, "block")) segment.block = seg.block;
6107 if (HOP(seg, "loop")) segment.loop = seg.loop;
6111 function must_visit(base, segment) {
6112 return base === segment || base.isPrototypeOf(segment);
6115 function mergeable(head, tail) {
6116 return must_visit(head.start, head.end) || must_visit(head.start, tail.start);
6120 function fill_holes(orig, elements) {
6121 for (var i = elements.length; --i >= 0;) {
6122 if (!elements[i]) elements[i] = make_node(AST_Hole, orig);
6126 function to_class_expr(defcl, drop_name) {
6127 var cl = make_node(AST_ClassExpression, defcl, defcl);
6128 cl.name = drop_name ? null : make_node(AST_SymbolClass, defcl.name, defcl.name);
6132 function to_func_expr(defun, drop_name) {
6134 switch (defun.CTOR) {
6135 case AST_AsyncDefun:
6136 ctor = AST_AsyncFunction;
6138 case AST_AsyncGeneratorDefun:
6139 ctor = AST_AsyncGeneratorFunction;
6142 ctor = AST_Function;
6144 case AST_GeneratorDefun:
6145 ctor = AST_GeneratorFunction;
6148 var fn = make_node(ctor, defun, defun);
6149 fn.name = drop_name ? null : make_node(AST_SymbolLambda, defun.name, defun.name);
6153 AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
6154 if (!compressor.option("unused")) return;
6156 var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
6157 var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
6158 var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
6159 var sym, nested = false;
6160 if (node instanceof AST_Assign) {
6161 if (node.write_only || node.operator == "=") sym = extract_reference(node.left, props);
6162 } else if (node instanceof AST_Unary) {
6163 if (node.write_only) sym = extract_reference(node.expression, props);
6165 if (!(sym instanceof AST_SymbolRef)) return;
6166 var def = sym.definition();
6167 if (export_defaults[def.id]) return;
6168 if (compressor.exposed(def)) return;
6169 if (!can_drop_symbol(sym, compressor, nested)) return;
6172 function extract_reference(node, props) {
6173 if (node instanceof AST_PropAccess) {
6174 var expr = node.expression;
6175 if (!expr.may_throw_on_access(compressor, true)) {
6177 if (props && node instanceof AST_Sub) props.unshift(node.property);
6178 return extract_reference(expr, props);
6180 } else if (node instanceof AST_Assign && node.operator == "=") {
6181 node.write_only = "p";
6182 var ref = extract_reference(node.right);
6183 if (!props) return ref;
6184 props.assign = node;
6185 return ref instanceof AST_SymbolRef ? ref : node.left;
6190 var assign_in_use = Object.create(null);
6191 var export_defaults = Object.create(null);
6192 var find_variable = function(name) {
6193 find_variable = compose(self, 0, noop);
6194 return find_variable(name);
6196 function compose(child, level, find) {
6197 var parent = compressor.parent(level);
6198 if (!parent) return find;
6199 var in_arg = parent instanceof AST_Lambda && member(child, parent.argnames);
6200 return compose(parent, level + 1, in_arg ? function(name) {
6201 var def = find(name);
6202 if (def) return def;
6203 def = parent.variables.get(name);
6205 var sym = def.orig[0];
6206 if (sym instanceof AST_SymbolFunarg || sym instanceof AST_SymbolLambda) return def;
6208 } : parent.variables ? function(name) {
6209 return find(name) || parent.variables.get(name);
6213 var for_ins = Object.create(null);
6215 var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
6216 var value_read = Object.create(null);
6217 var value_modified = Object.create(null);
6218 var var_defs = Object.create(null);
6219 if (self instanceof AST_Toplevel && compressor.top_retain) {
6220 self.variables.each(function(def) {
6221 if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
6222 in_use_ids[def.id] = true;
6227 var assignments = new Dictionary();
6228 var initializations = new Dictionary();
6229 // pass 1: find out which symbols are directly used in
6230 // this scope (not in nested scopes).
6232 var tw = new TreeWalker(function(node, descend) {
6233 if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) {
6234 node.each_argname(function(argname) {
6235 var def = argname.definition();
6236 if (!(def.id in in_use_ids)) {
6237 in_use_ids[def.id] = true;
6242 if (node === self) return;
6243 if (scope === self) {
6244 if (node instanceof AST_DefClass) {
6245 var def = node.name.definition();
6246 if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6247 in_use_ids[def.id] = true;
6250 if (node.extends) node.extends.walk(tw);
6251 var is_export = false;
6252 if (tw.parent() instanceof AST_ExportDefault) {
6254 export_defaults[def.id] = true;
6256 node.properties.forEach(function(prop) {
6257 if (prop.key instanceof AST_Node) prop.key.walk(tw);
6258 if (!prop.value) return;
6259 if (is_export || prop instanceof AST_ClassField && prop.static) {
6260 var save_scope = scope;
6262 prop.value.walk(tw);
6265 initializations.add(def.id, prop.value);
6270 if (node instanceof AST_LambdaDefinition) {
6271 var def = node.name.definition();
6272 if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6273 in_use_ids[def.id] = true;
6276 initializations.add(def.id, node);
6277 if (tw.parent() instanceof AST_ExportDefault) {
6278 export_defaults[def.id] = true;
6283 if (node instanceof AST_Definitions) {
6284 node.definitions.forEach(function(defn) {
6285 var value = defn.value;
6286 var side_effects = value
6287 && (defn.name instanceof AST_Destructured || value.has_side_effects(compressor));
6288 var shared = side_effects && value.tail_node().operator == "=";
6289 defn.name.mark_symbol(function(name) {
6290 if (!(name instanceof AST_SymbolDeclaration)) return;
6291 var def = name.definition();
6292 var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6293 if (node instanceof AST_Var && def.orig[0] instanceof AST_SymbolCatch) {
6294 var redef = def.redefined();
6295 if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1;
6297 if (!(def.id in in_use_ids) && (!drop_vars || def.exported
6298 || (node instanceof AST_Const ? def.redefined() : def.const_redefs)
6299 || !(node instanceof AST_Var || is_safe_lexical(def)))) {
6300 in_use_ids[def.id] = true;
6304 if (!side_effects) {
6305 initializations.add(def.id, value);
6306 } else if (shared) {
6307 verify_safe_usage(def, name, value_modified[def.id]);
6309 assignments.add(def.id, defn);
6313 if (side_effects) value.walk(tw);
6317 if (node instanceof AST_SymbolFunarg) {
6318 var def = node.definition();
6319 var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6320 assignments.add(def.id, node);
6323 if (node instanceof AST_SymbolImport) {
6324 var def = node.definition();
6325 if (!(def.id in in_use_ids) && (!drop_vars || !is_safe_lexical(def))) {
6326 in_use_ids[def.id] = true;
6332 return scan_ref_scoped(node, descend, true);
6334 tw.directives = Object.create(compressor.directives);
6336 var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie") ? function(def) {
6337 return !compressor.exposed(def) && def.references.length == def.replaced;
6339 if (!(def.id in in_use_ids)) return true;
6340 if (def.orig.length - def.eliminated < 2) return false;
6341 // function argument will always overshadow its name
6342 if (def.orig[1] instanceof AST_SymbolFunarg) return true;
6343 // retain if referenced within destructured object of argument
6344 return all(def.references, function(ref) {
6348 if (compressor.option("ie")) initializations.each(function(init, id) {
6349 if (id in in_use_ids) return;
6350 init.forEach(function(init) {
6351 init.walk(new TreeWalker(function(node) {
6352 if (node instanceof AST_Function && node.name && !drop_fn_name(node.name.definition())) {
6356 if (node instanceof AST_Scope) return true;
6360 // pass 2: for every used symbol we need to walk its
6361 // initialization code to figure out if it uses other
6362 // symbols (that may not be in_use).
6363 tw = new TreeWalker(scan_ref_scoped);
6364 for (var i = 0; i < in_use.length; i++) {
6365 var init = initializations.get(in_use[i].id);
6366 if (init) init.forEach(function(init) {
6370 Object.keys(assign_in_use).forEach(function(id) {
6371 var assigns = assign_in_use[id];
6373 delete assign_in_use[id];
6376 assigns = assigns.reduce(function(in_use, assigns) {
6377 assigns.forEach(function(assign) {
6378 push_uniq(in_use, assign);
6382 var in_use = (assignments.get(id) || []).filter(function(node) {
6383 return find_if(node instanceof AST_Unary ? function(assign) {
6384 return assign === node;
6385 } : function(assign) {
6386 if (assign === node) return true;
6387 if (assign instanceof AST_Unary) return false;
6388 return get_rvalue(assign) === get_rvalue(node);
6391 if (assigns.length == in_use.length) {
6392 assign_in_use[id] = in_use;
6394 delete assign_in_use[id];
6397 // pass 3: we should drop declarations not in_use
6398 var trim_defns = [];
6399 var unused_fn_names = [];
6400 var calls_to_drop_args = [];
6401 var fns_with_marked_args = [];
6402 var trimmer = new TreeTransformer(function(node) {
6403 if (node instanceof AST_DefaultValue) return trim_default(trimmer, node);
6404 if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer);
6405 if (node instanceof AST_DestructuredArray) {
6406 var trim = !node.rest;
6407 for (var i = node.elements.length; --i >= 0;) {
6408 var element = node.elements[i].transform(trimmer);
6410 node.elements[i] = element;
6413 node.elements.pop();
6415 node.elements[i] = make_node(AST_Hole, node.elements[i]);
6420 if (node instanceof AST_DestructuredObject) {
6421 var properties = [];
6422 node.properties.forEach(function(prop) {
6424 if (prop.key instanceof AST_Node) {
6425 prop.key = prop.key.transform(tt);
6426 retain = prop.key.has_side_effects(compressor);
6428 if ((retain || node.rest) && is_decl(prop.value)) {
6429 prop.value = prop.value.transform(tt);
6430 properties.push(prop);
6432 var value = prop.value.transform(trimmer);
6433 if (!value && node.rest) {
6434 if (prop.value instanceof AST_DestructuredArray) {
6435 value = make_node(AST_DestructuredArray, prop.value, { elements: [] });
6437 value = make_node(AST_DestructuredObject, prop.value, { properties: [] });
6442 properties.push(prop);
6446 node.properties = properties;
6449 if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
6451 var tt = new TreeTransformer(function(node, descend, in_list) {
6452 var parent = tt.parent();
6454 var props = [], sym = assign_as_unused(node, props);
6457 if (can_drop_lhs(sym, node)) {
6458 if (node instanceof AST_Assign) {
6459 value = get_rhs(node);
6460 if (node.write_only === true) value = value.drop_side_effect_free(compressor);
6462 if (!value) value = make_node(AST_Number, node, { value: 0 });
6466 var assign = props.assign.drop_side_effect_free(compressor);
6468 assign.write_only = true;
6469 props.unshift(assign);
6472 if (!(parent instanceof AST_Sequence)
6473 || parent.tail_node() === node
6474 || value.has_side_effects(compressor)) {
6477 switch (props.length) {
6481 return maintain_this_binding(compressor, parent, node, props[0].transform(tt));
6483 return make_sequence(node, props.map(function(prop) {
6484 return prop.transform(tt);
6488 } else if (node instanceof AST_UnaryPostfix
6489 && node.expression instanceof AST_SymbolRef
6490 && indexOf_assign(node.expression.definition(), node) < 0) {
6491 return make_node(AST_UnaryPrefix, node, {
6493 expression: node.expression
6497 if (node instanceof AST_Call) calls_to_drop_args.push(node);
6498 if (scope !== self) return;
6499 if (drop_funcs && node !== self && node instanceof AST_DefClass) {
6500 var def = node.name.definition();
6501 if (!(def.id in in_use_ids)) {
6502 log(node.name, "Dropping unused class {name}");
6505 if (parent instanceof AST_ExportDefault) return to_class_expr(node, true);
6506 var trimmed = node.drop_side_effect_free(compressor, true);
6507 if (trimmed === node) trimmed = to_class_expr(node, true);
6508 if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed });
6509 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6512 if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) {
6513 unused_fn_names.push(node);
6515 if (node instanceof AST_Lambda) {
6516 if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
6517 var def = node.name.definition();
6518 if (!(def.id in in_use_ids)) {
6519 log(node.name, "Dropping unused function {name}");
6521 if (parent instanceof AST_ExportDefault) {
6523 return to_func_expr(node, true);
6525 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6528 if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
6529 unused_fn_names.push(node);
6531 if (!(node instanceof AST_Accessor)) {
6533 var rest = node.rest.transform(trimmer);
6534 if (rest instanceof AST_Destructured && !rest.rest
6535 && (!node.uses_arguments || tt.has_directive("use strict"))) {
6536 if (rest instanceof AST_DestructuredArray) {
6537 if (rest.elements.length == 0) rest = null;
6538 } else if (rest.properties.length == 0) {
6544 var argnames = node.argnames;
6545 var trim = compressor.drop_fargs(node, parent) && !node.rest;
6546 var default_length = trim ? -1 : node.length();
6547 for (var i = argnames.length; --i >= 0;) {
6548 var sym = argnames[i];
6549 if (!(sym instanceof AST_SymbolFunarg)) {
6550 var arg = sym.transform(trimmer);
6554 log(sym.name, "Dropping unused default argument {name}");
6556 } else if (i > default_length) {
6557 log(sym.name, "Dropping unused default argument assignment {name}");
6558 sym.name.__unused = true;
6559 argnames[i] = sym.name;
6561 log(sym.name, "Dropping unused default argument value {name}");
6562 sym.value = make_node(AST_Number, sym, { value: 0 });
6566 var def = sym.definition();
6567 if (def.id in in_use_ids) {
6569 if (indexOf_assign(def, sym) < 0) sym.__unused = null;
6571 log(sym, "Dropping unused function argument {name}");
6574 sym.__unused = true;
6577 fns_with_marked_args.push(node);
6580 if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
6581 node.argname.transform(trimmer);
6583 if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) {
6584 // place uninitialized names at the start
6585 var body = [], head = [], tail = [];
6586 // for unused names whose initialization has
6587 // side effects, we can cascade the init. code
6588 // into the next one, or next statement.
6589 var side_effects = [];
6591 var is_var = node instanceof AST_Var;
6592 node.definitions.forEach(function(def) {
6593 if (def.value) def.value = def.value.transform(tt);
6594 var value = def.value;
6595 if (def.name instanceof AST_Destructured) {
6596 var trimmed = trim_destructured(def.name, value, function(node) {
6597 if (!drop_vars) return node;
6598 if (node.definition().id in in_use_ids) return node;
6599 if (is_catch(node)) return node;
6600 if (is_var && !can_drop_symbol(node)) return node;
6604 def = make_node(AST_VarDef, def, {
6606 value: value = trimmed.value,
6609 } else if (trimmed.value) {
6610 side_effects.push(trimmed.value);
6614 var sym = def.name.definition();
6615 var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym);
6616 if (!drop_sym || !drop_vars || sym.id in in_use_ids) {
6617 if (value && indexOf_assign(sym, def) < 0) {
6618 value = value.drop_side_effect_free(compressor);
6620 AST_Node.warn("Side effects in last use of variable {name} [{file}:{line},{col}]", template(def.name));
6621 side_effects.push(value);
6624 trim_defns.push(def);
6627 if (!value && !(node instanceof AST_Let)) {
6628 if (parent instanceof AST_ExportDeclaration) {
6630 } else if (drop_sym && var_defs[sym.id] > 1) {
6631 AST_Node.info("Dropping declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6637 } else if (compressor.option("functions")
6638 && !compressor.option("ie")
6640 && var_defs[sym.id] == 1
6641 && sym.assignments == 0
6642 && value instanceof AST_LambdaExpression
6643 && !is_arguments(sym)
6645 && assigned_once(value, sym.references)
6646 && can_declare_defun(value)
6647 && (old_def = rename_def(value, def.name.name)) !== false) {
6648 AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
6650 switch (value.CTOR) {
6651 case AST_AsyncFunction:
6652 ctor = AST_AsyncDefun;
6654 case AST_AsyncGeneratorFunction:
6655 ctor = AST_AsyncGeneratorDefun;
6660 case AST_GeneratorFunction:
6661 ctor = AST_GeneratorDefun;
6664 var defun = make_node(ctor, def, value);
6665 defun.name = make_node(AST_SymbolDefun, def.name, def.name);
6666 var name_def = def.name.scope.resolve().def_function(defun.name);
6667 if (old_def) old_def.forEach(function(node) {
6668 node.name = name_def.name;
6669 node.thedef = name_def;
6675 && var_defs[sym.id] > 1
6676 && !(parent instanceof AST_ExportDeclaration)
6677 && sym.orig.indexOf(def.name) > sym.eliminated) {
6683 } else if (is_catch(def.name)) {
6684 value = value && value.drop_side_effect_free(compressor);
6685 if (value) side_effects.push(value);
6686 if (var_defs[sym.id] > 1) {
6687 AST_Node.warn("Dropping duplicated declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6695 value = value && !value.single_use && value.drop_side_effect_free(compressor);
6697 AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
6698 side_effects.push(value);
6700 log(def.name, "Dropping unused variable {name}");
6705 function assigned_once(fn, refs) {
6706 if (refs.length == 0) return fn === def.name.fixed_value();
6707 return all(refs, function(ref) {
6708 return fn === ref.fixed_value();
6712 function can_declare_defun(fn) {
6713 if (!is_var || compressor.has_directive("use strict") || !(fn instanceof AST_Function)) {
6714 return parent instanceof AST_Scope;
6716 return parent instanceof AST_Block
6717 || parent instanceof AST_For && parent.init === node
6718 || parent instanceof AST_If;
6721 function rename_def(fn, name) {
6722 if (!fn.name) return null;
6723 var def = fn.name.definition();
6724 if (def.orig.length > 1) return null;
6725 if (def.assignments > 0) return false;
6726 if (def.name == name) return def;
6730 forbidden = is_async;
6733 forbidden = is_generator;
6736 return all(def.references, function(ref) {
6737 var scope = ref.scope;
6738 if (scope.find_variable(name) !== sym) return false;
6740 scope = scope.resolve();
6741 if (forbidden(scope)) return false;
6742 } while (scope !== fn && (scope = scope.parent_scope));
6747 function is_catch(node) {
6748 var sym = node.definition();
6749 return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve();
6753 if (side_effects.length > 0) {
6754 if (tail.length == 0) {
6755 body.push(make_node(AST_SimpleStatement, node, {
6756 body: make_sequence(node, side_effects),
6759 side_effects.push(value);
6760 def.value = make_sequence(value, side_effects);
6762 def.value = make_node(AST_UnaryPrefix, def, {
6764 expression: make_sequence(def, side_effects),
6772 switch (head.length) {
6774 if (tail.length == 0) break;
6775 if (tail.length == duplicated) {
6776 [].unshift.apply(side_effects, tail.map(function(def) {
6777 AST_Node.info("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
6778 var sym = def.name.definition();
6779 var ref = make_node(AST_SymbolRef, def.name, def.name);
6780 sym.references.push(ref);
6781 var assign = make_node(AST_Assign, def, {
6786 var index = indexOf_assign(sym, def);
6787 if (index >= 0) assign_in_use[sym.id][index] = assign;
6794 if (tail.length == 0) {
6795 var id = head[0].name.definition().id;
6796 if (id in for_ins) {
6797 node.definitions = head;
6798 for_ins[id].init = node;
6803 node.definitions = head.concat(tail);
6806 if (side_effects.length > 0) {
6807 body.push(make_node(AST_SimpleStatement, node, {
6808 body: make_sequence(node, side_effects)
6811 return insert_statements(body, node, in_list);
6813 if (node instanceof AST_Assign) {
6815 if (!(node.left instanceof AST_Destructured)) return node;
6816 var trimmed = trim_destructured(node.left, node.right, function(node) {
6818 }, node.write_only === true);
6819 if (trimmed.name) return make_node(AST_Assign, node, {
6820 operator: node.operator,
6822 right: trimmed.value,
6824 if (trimmed.value) return trimmed.value;
6825 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return List.skip;
6826 return make_node(AST_Number, node, { value: 0 });
6828 if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
6829 // Certain combination of unused name + side effect leads to invalid AST:
6830 // https://github.com/mishoo/UglifyJS/issues/1830
6831 // We fix it at this stage by moving the label inwards, back to the `for`.
6833 if (node.body instanceof AST_BlockStatement) {
6834 var block = node.body;
6835 node.body = block.body.pop();
6836 block.body.push(node);
6837 return in_list ? List.splice(block.body) : block;
6841 if (node instanceof AST_Scope) {
6845 if (node instanceof AST_SymbolImport) {
6846 if (!compressor.option("imports") || node.definition().id in in_use_ids) return node;
6847 return in_list ? List.skip : null;
6850 function descend_scope() {
6851 var save_scope = scope;
6856 }, function(node, in_list) {
6857 if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
6858 // Certain combination of unused name + side effect leads to invalid AST:
6859 // https://github.com/mishoo/UglifyJS/issues/44
6860 // https://github.com/mishoo/UglifyJS/issues/1838
6861 // https://github.com/mishoo/UglifyJS/issues/3371
6862 // We fix it at this stage by moving the `var` outside the `for`.
6863 if (node instanceof AST_For) {
6865 if (node.init instanceof AST_BlockStatement) {
6867 node.init = block.body.pop();
6868 block.body.push(node);
6870 if (node.init instanceof AST_Defun) {
6872 block = make_node(AST_BlockStatement, node, {
6876 block.body.splice(-1, 0, node.init);
6878 } else if (node.init instanceof AST_SimpleStatement) {
6879 node.init = node.init.body;
6880 } else if (is_empty(node.init)) {
6883 return !block ? node : in_list ? List.splice(block.body) : block;
6885 if (node instanceof AST_ForIn) {
6886 if (!drop_vars || !compressor.option("loops")) return;
6887 if (!is_empty(node.body)) return;
6888 var sym = get_init_symbol(node);
6890 var def = sym.definition();
6891 if (def.id in in_use_ids) return;
6892 log(sym, "Dropping unused loop variable {name}");
6893 if (for_ins[def.id] === node) delete for_ins[def.id];
6895 var value = node.object.drop_side_effect_free(compressor);
6897 AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", value.start);
6898 body.push(make_node(AST_SimpleStatement, node, {
6902 if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) {
6903 body.push(node.init);
6905 return insert_statements(body, node, in_list);
6907 if (node instanceof AST_Import) {
6908 if (node.properties && node.properties.length == 0) node.properties = null;
6911 if (node instanceof AST_Sequence) {
6912 if (node.expressions.length > 1) return;
6913 return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
6916 tt.push(compressor.parent());
6918 if (self instanceof AST_Lambda
6919 && self.body.length == 1
6920 && self.body[0] instanceof AST_Directive
6921 && self.body[0].value == "use strict") {
6922 self.body.length = 0;
6924 trim_defns.forEach(function(def) {
6927 unused_fn_names.forEach(function(fn) {
6930 calls_to_drop_args.forEach(function(call) {
6931 drop_unused_call_args(call, compressor, fns_with_marked_args);
6934 function log(sym, text) {
6935 AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym));
6938 function template(sym) {
6941 file: sym.start.file,
6942 line: sym.start.line,
6943 col : sym.start.col,
6947 function get_rvalue(expr) {
6948 return expr[expr instanceof AST_Assign ? "right" : "value"];
6951 function insert_statements(body, orig, in_list) {
6952 switch (body.length) {
6954 return in_list ? List.skip : make_node(AST_EmptyStatement, orig);
6958 return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, {
6964 function track_assigns(def, node) {
6965 if (def.scope.resolve() !== self) return false;
6966 if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
6967 return assign_in_use[def.id] !== false;
6970 function add_assigns(def, node) {
6971 if (!assign_in_use[def.id]) assign_in_use[def.id] = [];
6972 if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns);
6975 function indexOf_assign(def, node) {
6976 var nodes = assign_in_use[def.id];
6977 return nodes && nodes.indexOf(node);
6980 function verify_safe_usage(def, read, modified) {
6981 if (def.id in in_use_ids) return;
6982 if (read && modified) {
6983 in_use_ids[def.id] = read;
6986 value_read[def.id] = read;
6987 value_modified[def.id] = modified;
6991 function can_drop_lhs(sym, node) {
6992 var def = sym.definition();
6993 var in_use = in_use_ids[def.id];
6994 if (!in_use) return true;
6995 if (node[node instanceof AST_Assign ? "left" : "expression"] !== sym) return false;
6996 return in_use === sym && def.references.length - def.replaced == 1 || indexOf_assign(def, node) < 0;
6999 function get_rhs(assign) {
7000 var rhs = assign.right;
7001 if (!assign.write_only) return rhs;
7002 if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
7003 if (!(rhs.left instanceof AST_SymbolRef)) return rhs;
7004 if (!(assign.left instanceof AST_SymbolRef)) return rhs;
7005 var def = assign.left.definition();
7006 if (rhs.left.definition() !== def) return rhs;
7007 if (rhs.right.has_side_effects(compressor)) return rhs;
7008 if (track_assigns(def, rhs.left)) add_assigns(def, rhs.left);
7012 function get_init_symbol(for_in) {
7013 var init = for_in.init;
7014 if (init instanceof AST_Definitions) {
7015 init = init.definitions[0].name;
7016 return init instanceof AST_SymbolDeclaration && init;
7018 while (init instanceof AST_PropAccess) init = init.expression.tail_node();
7019 if (init instanceof AST_SymbolRef) return init;
7022 function scan_ref_scoped(node, descend, init) {
7023 if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
7024 var def = node.left.definition();
7025 if (def.scope.resolve() === self) assignments.add(def.id, node);
7027 if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
7028 var def = node.expression.definition();
7029 if (def.scope.resolve() === self) assignments.add(def.id, node);
7031 var node_def, props = [], sym = assign_as_unused(node, props);
7032 if (sym && ((node_def = sym.definition()).scope.resolve() === self
7033 || self.variables.get(sym.name) === node_def)
7034 && !(is_arguments(node_def) && !all(self.argnames, function(argname) {
7035 return !argname.match_symbol(function(node) {
7036 if (node instanceof AST_SymbolFunarg) {
7037 var def = node.definition();
7038 return def.references.length > def.replaced;
7042 if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return;
7043 var assign = props.assign;
7045 assign.write_only = true;
7048 props.forEach(function(prop) {
7051 if (node instanceof AST_Assign) {
7052 var right = get_rhs(node), shared = false;
7053 if (init && node.write_only === true && !right.has_side_effects(compressor)) {
7054 initializations.add(node_def.id, right);
7057 shared = right.tail_node().operator == "=";
7059 if (node.left === sym) {
7060 if (!node.write_only || shared) {
7061 verify_safe_usage(node_def, sym, value_modified[node_def.id]);
7064 var fixed = sym.fixed_value();
7065 if (!fixed || !fixed.is_constant()) {
7066 verify_safe_usage(node_def, value_read[node_def.id], true);
7070 if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
7073 if (node instanceof AST_ForIn) {
7074 if (node.init instanceof AST_SymbolRef && scope === self) {
7075 var id = node.init.definition().id;
7076 if (!(id in for_ins)) for_ins[id] = node;
7078 if (!drop_vars || !compressor.option("loops")) return;
7079 if (!is_empty(node.body)) return;
7080 if (node.init.has_side_effects(compressor)) return;
7081 var sym = get_init_symbol(node);
7083 var def = sym.definition();
7084 if (def.scope.resolve() !== self) {
7085 var d = find_variable(sym.name);
7086 if (d === def || d && d.redefined() === def) return;
7088 node.object.walk(tw);
7091 if (node instanceof AST_SymbolRef) {
7092 node_def = node.definition();
7093 if (!(node_def.id in in_use_ids)) {
7094 in_use_ids[node_def.id] = true;
7095 in_use.push(node_def);
7097 if (cross_scope(node_def.scope, node.scope)) {
7098 var redef = node_def.redefined();
7099 if (redef && !(redef.id in in_use_ids)) {
7100 in_use_ids[redef.id] = true;
7104 if (track_assigns(node_def, node)) add_assigns(node_def, node);
7107 if (node instanceof AST_Scope) {
7108 var save_scope = scope;
7116 function is_decl(node) {
7117 return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration;
7120 function trim_default(trimmer, node) {
7121 node.value = node.value.transform(tt);
7122 var name = node.name.transform(trimmer);
7124 if (node.name instanceof AST_Destructured) return null;
7125 var value = node.value.drop_side_effect_free(compressor);
7126 if (!value) return null;
7127 log(node.name, "Side effects in default value of unused variable {name}");
7128 node.name.__unused = null;
7134 function trim_destructured(node, value, process, drop) {
7135 var trimmer = new TreeTransformer(function(node) {
7136 if (node instanceof AST_DefaultValue) {
7137 if (compressor.option("default_values") && value && value.is_defined(compressor)) {
7140 var save_drop = drop;
7142 var trimmed = trim_default(trimmer, node);
7144 if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor);
7148 if (node instanceof AST_DestructuredArray) {
7149 var save_drop = drop;
7150 var save_value = value;
7151 if (value instanceof AST_SymbolRef) {
7153 value = value.fixed_value();
7155 var values = value instanceof AST_Array && value.elements;
7156 var elements = [], newValues = drop && [], pos = 0;
7157 node.elements.forEach(function(element, index) {
7158 value = values && values[index];
7159 if (value instanceof AST_Hole) {
7161 } else if (value instanceof AST_Spread) {
7163 newValues.length = pos;
7164 fill_holes(save_value, newValues);
7165 [].push.apply(newValues, values.slice(index));
7166 save_value.elements = newValues;
7168 value = values = false;
7170 element = element.transform(trimmer);
7171 if (element) elements[pos] = element;
7172 if (drop && value) newValues[pos] = value;
7173 if (element || value || !drop || !values) pos++;
7175 value = values && make_node(AST_Array, save_value, {
7176 elements: values.slice(node.elements.length),
7179 var was_drop = drop;
7181 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7183 if (node.rest) elements.length = pos;
7186 if (value && !node.rest) value = value.drop_side_effect_free(compressor);
7187 if (value instanceof AST_Array) {
7188 value = value.elements;
7189 } else if (value instanceof AST_Sequence) {
7190 value = value.expressions;
7194 if (value && value.length) {
7195 newValues.length = pos;
7196 [].push.apply(newValues, value);
7201 if (values && newValues) {
7202 fill_holes(value, newValues);
7203 value.elements = newValues;
7205 if (!node.rest && (value instanceof AST_Array
7206 || value && value.is_string(compressor))) switch (elements.length) {
7208 if (drop) value = value.drop_side_effect_free(compressor);
7212 var sym = elements[0];
7213 if (sym.has_side_effects(compressor)) break;
7214 value = make_node(AST_Sub, node, {
7216 property: make_node(AST_Number, node, { value: 0 }),
7220 fill_holes(node, elements);
7221 node.elements = elements;
7224 if (node instanceof AST_DestructuredObject) {
7225 var save_drop = drop;
7226 var save_value = value;
7227 if (value instanceof AST_SymbolRef) {
7229 value = value.fixed_value();
7231 var prop_keys, prop_map;
7232 if (value instanceof AST_Object) {
7234 prop_map = Object.create(null);
7235 value.properties.forEach(function(prop, index) {
7236 if (prop instanceof AST_Spread) return prop_map = false;
7238 if (key instanceof AST_Node) key = key.evaluate(compressor, true);
7239 if (key instanceof AST_Node) {
7241 } else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
7242 prop_map[key] = prop;
7244 prop_keys[index] = key;
7249 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7251 var can_drop = Object.create(null);
7252 var drop_keys = drop && Object.create(null);
7253 var properties = [];
7254 node.properties.map(function(prop) {
7256 if (key instanceof AST_Node) {
7257 prop.key = key = key.transform(tt);
7258 key = key.evaluate(compressor, true);
7260 if (key instanceof AST_Node) {
7263 can_drop[key] = !(key in can_drop);
7266 }).forEach(function(key, index) {
7267 var prop = node.properties[index], trimmed;
7268 if (key instanceof AST_Node) {
7271 trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
7273 drop = drop_keys && can_drop[key];
7274 var mapped = prop_map && prop_map[key];
7276 value = mapped.value;
7277 if (value instanceof AST_Accessor) value = false;
7281 trimmed = prop.value.transform(trimmer);
7283 if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
7284 if (drop_keys && !(key in drop_keys)) {
7286 drop_keys[key] = mapped;
7287 if (value === null) {
7288 prop_map[key] = retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
7290 value: make_node(AST_Number, mapped, { value: 0 }),
7294 drop_keys[key] = true;
7297 } else if (drop_keys) {
7298 drop_keys[key] = false;
7300 if (value) mapped.value = value;
7303 prop.value = trimmed;
7304 properties.push(prop);
7309 if (drop_keys && prop_keys) value.properties = List(value.properties, function(prop, index) {
7310 if (prop instanceof AST_Spread) return prop;
7311 var key = prop_keys[index];
7312 if (key instanceof AST_Node) return prop;
7313 if (key in drop_keys) {
7314 var mapped = drop_keys[key];
7315 if (!mapped) return prop;
7316 if (mapped === prop) return prop_map[key] || List.skip;
7317 } else if (node.rest) {
7320 var trimmed = prop.value.drop_side_effect_free(compressor);
7322 prop.value = trimmed;
7325 return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, {
7327 value: make_node(AST_Number, prop, { value: 0 }),
7330 if (value && !node.rest) switch (properties.length) {
7332 if (value.may_throw_on_access(compressor, true)) break;
7333 if (drop) value = value.drop_side_effect_free(compressor);
7337 var prop = properties[0];
7338 if (prop.key instanceof AST_Node) break;
7339 if (prop.value.has_side_effects(compressor)) break;
7340 value = make_node(AST_Sub, node, {
7342 property: make_node_from_constant(prop.key, prop),
7346 node.properties = properties;
7349 if (node instanceof AST_Hole) {
7352 node = process(node);
7354 if (!node && drop && value) value = value.drop_side_effect_free(compressor);
7358 name: node.transform(trimmer),
7362 function retain_key(prop) {
7363 return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor);
7366 function retain_lhs(node) {
7367 if (node instanceof AST_DefaultValue) return retain_lhs(node.name);
7368 if (node instanceof AST_Destructured) {
7369 if (value === null) {
7370 value = make_node(AST_Number, node, { value: 0 });
7371 } else if (value && (value.tail_node().write_only === true
7372 || value.may_throw_on_access(compressor, true))) {
7373 value = make_node(AST_Array, node, {
7374 elements: value instanceof AST_Sequence ? value.expressions : [ value ],
7377 return make_node(AST_DestructuredObject, node, { properties: [] });
7379 node.__unused = null;
7385 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
7386 if (compressor.has_directive("use asm")) return;
7387 var hoist_funs = compressor.option("hoist_funs");
7388 var hoist_vars = compressor.option("hoist_vars");
7391 // let's count var_decl first, we seem to waste a lot of
7392 // space if we hoist `var` when there's only one.
7394 self.walk(new TreeWalker(function(node) {
7395 if (var_decl > 1) return true;
7396 if (node instanceof AST_ExportDeclaration) return true;
7397 if (node instanceof AST_Scope && node !== self) return true;
7398 if (node instanceof AST_Var) {
7403 if (var_decl <= 1) hoist_vars = false;
7405 if (!hoist_funs && !hoist_vars) return;
7406 var consts = Object.create(null);
7409 var vars = new Dictionary(), vars_found = 0;
7410 var tt = new TreeTransformer(function(node, descend, in_list) {
7411 if (node === self) return;
7412 if (node instanceof AST_Directive) {
7414 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7416 if (node instanceof AST_LambdaDefinition) {
7417 if (!hoist_funs) return node;
7418 var p = tt.parent();
7419 if (p instanceof AST_ExportDeclaration) return node;
7420 if (p instanceof AST_ExportDefault) return node;
7421 if (p !== self && compressor.has_directive("use strict")) return node;
7423 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7425 if (node instanceof AST_Var) {
7426 if (!hoist_vars) return node;
7427 var p = tt.parent();
7428 if (p instanceof AST_ExportDeclaration) return node;
7429 if (!all(node.definitions, function(defn) {
7430 var sym = defn.name;
7431 return sym instanceof AST_SymbolVar
7432 && !consts[sym.name]
7433 && self.find_variable(sym.name) === sym.definition();
7435 node.definitions.forEach(function(def) {
7436 vars.set(def.name.name, def);
7439 var seq = node.to_assignments();
7440 if (p instanceof AST_ForEnumeration && p.init === node) {
7441 if (seq) return seq;
7442 var def = node.definitions[0].name;
7443 return make_node(AST_SymbolRef, def, def);
7445 if (p instanceof AST_For && p.init === node) return seq;
7446 if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7447 return make_node(AST_SimpleStatement, node, { body: seq });
7449 if (node instanceof AST_Scope) return node;
7450 if (node instanceof AST_SymbolConst) {
7451 consts[node.name] = true;
7456 if (vars_found > 0) {
7457 // collect only vars which don't show up in self's arguments list
7459 if (self instanceof AST_Lambda) self.each_argname(function(argname) {
7460 vars.del(argname.name);
7462 vars.each(function(def, name) {
7466 vars.set(name, def);
7468 if (defs.length > 0) {
7469 // try to merge in assignments
7470 insert_vars(self.body);
7471 defs = make_node(AST_Var, self, { definitions: defs });
7475 self.body = dirs.concat(hoisted, self.body);
7477 function insert_vars(body) {
7478 while (body.length) {
7480 if (stat instanceof AST_SimpleStatement) {
7481 var expr = stat.body, sym, assign;
7482 if (expr instanceof AST_Assign
7483 && expr.operator == "="
7484 && (sym = expr.left) instanceof AST_Symbol
7485 && vars.has(sym.name)) {
7486 var def = vars.get(sym.name);
7487 if (def.value) break;
7488 var value = expr.right;
7489 if (value instanceof AST_Sequence) value = value.clone();
7496 if (expr instanceof AST_Sequence
7497 && (assign = expr.expressions[0]) instanceof AST_Assign
7498 && assign.operator == "="
7499 && (sym = assign.left) instanceof AST_Symbol
7500 && vars.has(sym.name)) {
7501 var def = vars.get(sym.name);
7502 if (def.value) break;
7503 def.value = assign.right;
7506 stat.body = make_sequence(expr, expr.expressions.slice(1));
7510 if (stat instanceof AST_EmptyStatement) {
7514 if (stat instanceof AST_BlockStatement && !insert_vars(stat.body)) {
7524 function scan_local_returns(fn, transform) {
7525 fn.walk(new TreeWalker(function(node) {
7526 if (node instanceof AST_Return) {
7530 if (node instanceof AST_Scope && node !== fn) return true;
7534 function map_bool_returns(fn) {
7535 var map = Object.create(null);
7536 scan_local_returns(fn, function(node) {
7537 var value = node.value;
7538 if (value) value = value.tail_node();
7539 if (value instanceof AST_SymbolRef) {
7540 var id = value.definition().id;
7541 map[id] = (map[id] || 0) + 1;
7547 function all_bool(def, bool_returns, compressor) {
7548 return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length - def.replaced
7549 && !compressor.exposed(def);
7552 function process_boolean_returns(fn, compressor) {
7553 scan_local_returns(fn, function(node) {
7554 node.in_bool = true;
7555 var value = node.value;
7557 var ev = fuzzy_eval(compressor, value);
7559 value = value.drop_side_effect_free(compressor);
7560 node.value = value ? make_sequence(node.value, [
7562 make_node(AST_Number, node.value, { value: 0 }),
7564 } else if (!(ev instanceof AST_Node)) {
7565 value = value.drop_side_effect_free(compressor);
7566 node.value = value ? make_sequence(node.value, [
7568 make_node(AST_Number, node.value, { value: 1 }),
7569 ]) : make_node(AST_Number, node.value, { value: 1 });
7575 AST_Scope.DEFMETHOD("process_boolean_returns", noop);
7576 AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
7577 if (!compressor.option("booleans")) return;
7578 var bool_returns = map_bool_returns(this);
7579 if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
7580 if (compressor.parent() instanceof AST_ExportDefault) return;
7581 process_boolean_returns(this, compressor);
7583 AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
7584 if (!compressor.option("booleans")) return;
7585 var bool_returns = map_bool_returns(this);
7586 if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
7587 var parent = compressor.parent();
7588 if (parent instanceof AST_Assign) {
7589 if (parent.operator != "=") return;
7590 var sym = parent.left;
7591 if (!(sym instanceof AST_SymbolRef)) return;
7592 if (!all_bool(sym.definition(), bool_returns, compressor)) return;
7593 } else if (parent instanceof AST_Call && parent.expression !== this) {
7594 var exp = parent.expression;
7595 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
7596 if (!(exp instanceof AST_Lambda)) return;
7597 if (exp.uses_arguments || exp.pinned()) return;
7598 var sym = exp.argnames[parent.args.indexOf(this)];
7599 if (sym instanceof AST_DefaultValue) sym = sym.name;
7600 if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
7601 } else if (parent.TYPE == "Call") {
7603 var in_bool = compressor.in_boolean_context();
7604 compressor.push(this);
7605 if (!in_bool) return;
7607 process_boolean_returns(this, compressor);
7610 AST_BlockScope.DEFMETHOD("var_names", function() {
7611 var var_names = this._var_names;
7613 this._var_names = var_names = Object.create(null);
7614 this.enclosed.forEach(function(def) {
7615 var_names[def.name] = true;
7617 this.variables.each(function(def, name) {
7618 var_names[name] = true;
7624 AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
7625 var scopes = [ this ];
7626 if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
7628 if (member(s, scopes)) return;
7630 push_uniq(scopes, s);
7632 } while (s && s !== this);
7634 prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
7636 for (var i = 0; !all(scopes, function(scope) {
7637 return !scope.var_names()[name];
7638 }); i++) name = prefix + "$" + i;
7639 var sym = make_node(type, orig, {
7643 var def = this.def_variable(sym);
7644 scopes.forEach(function(scope) {
7645 scope.enclosed.push(def);
7646 scope.var_names()[name] = true;
7651 AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
7652 if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
7654 var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
7655 var defs_by_id = Object.create(null);
7656 self.transform(new TreeTransformer(function(node, descend) {
7657 if (node instanceof AST_Assign) {
7658 if (node.operator != "=") return;
7659 if (!node.write_only) return;
7660 if (!can_hoist(node.left, node.right, 1)) return;
7661 descend(node, this);
7662 var defs = new Dictionary();
7663 var assignments = [];
7665 node.right.properties.forEach(function(prop) {
7666 var decl = make_sym(AST_SymbolVar, node.left, prop.key);
7667 decls.push(make_node(AST_VarDef, node, {
7671 var sym = make_node(AST_SymbolRef, node, {
7674 thedef: decl.definition(),
7677 assignments.push(make_node(AST_Assign, node, {
7683 defs.value = node.right;
7684 defs_by_id[node.left.definition().id] = defs;
7685 self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
7688 return make_sequence(node, assignments);
7690 if (node instanceof AST_Scope) return node === self ? undefined : node;
7691 if (node instanceof AST_VarDef) {
7692 if (!can_hoist(node.name, node.value, 0)) return;
7693 descend(node, this);
7694 var defs = new Dictionary();
7696 var decl = node.clone();
7697 decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null;
7698 var_defs.push(decl);
7699 node.value.properties.forEach(function(prop) {
7700 var_defs.push(make_node(AST_VarDef, node, {
7701 name: make_sym(node.name.CTOR, node.name, prop.key),
7705 defs.value = node.value;
7706 defs_by_id[node.name.definition().id] = defs;
7707 return List.splice(var_defs);
7710 function make_sym(type, sym, key) {
7711 var new_var = self.make_var(type, sym, sym.name + "_" + key);
7712 defs.set(key, new_var.definition());
7716 self.transform(new TreeTransformer(function(node, descend) {
7717 if (node instanceof AST_PropAccess) {
7718 if (!(node.expression instanceof AST_SymbolRef)) return;
7719 var defs = defs_by_id[node.expression.definition().id];
7721 if (node.expression.fixed_value() !== defs.value) return;
7722 var def = defs.get(node.get_property());
7723 var sym = make_node(AST_SymbolRef, node, {
7725 scope: node.expression.scope,
7731 if (node instanceof AST_SymbolRef) {
7732 var defs = defs_by_id[node.definition().id];
7734 if (node.fixed_value() !== defs.value) return;
7735 return make_node(AST_Object, node, { properties: [] });
7739 function can_hoist(sym, right, count) {
7740 if (!(sym instanceof AST_Symbol)) return;
7741 var def = sym.definition();
7742 if (def.assignments != count) return;
7743 if (def.references.length - def.replaced == count) return;
7744 if (def.single_use) return;
7745 if (top_retain(def)) return;
7746 if (sym.fixed_value() !== right) return;
7747 var fixed = sym.fixed || def.fixed;
7748 if (fixed.direct_access) return;
7749 if (fixed.escaped && fixed.escaped.depth == 1) return;
7750 return right instanceof AST_Object
7751 && right.properties.length > 0
7752 && all(right.properties, can_hoist_property)
7753 && can_drop_symbol(sym, compressor);
7757 function fn_name_unused(fn, compressor) {
7758 if (!fn.name || !compressor.option("ie")) return true;
7759 var def = fn.name.definition();
7760 if (compressor.exposed(def)) return false;
7761 return all(def.references, function(sym) {
7762 return !(sym instanceof AST_SymbolRef);
7766 // drop_side_effect_free()
7767 // remove side-effect-free parts which only affects return value
7769 // Drop side-effect-free elements from an array of expressions.
7770 // Returns an array of expressions with side-effects or null
7771 // if all elements were dropped. Note: original array may be
7772 // returned if nothing changed.
7773 function trim(nodes, compressor, first_in_statement, spread) {
7774 var len = nodes.length;
7775 var ret = [], changed = false;
7776 for (var i = 0; i < len; i++) {
7777 var node = nodes[i];
7779 if (spread && node instanceof AST_Spread) {
7780 trimmed = spread(node, compressor, first_in_statement);
7782 trimmed = node.drop_side_effect_free(compressor, first_in_statement);
7784 if (trimmed !== node) changed = true;
7787 first_in_statement = false;
7790 return ret.length ? changed ? ret : nodes : null;
7792 function array_spread(node, compressor, first_in_statement) {
7793 var exp = node.expression;
7794 if (!exp.is_string(compressor)) return node;
7795 return exp.drop_side_effect_free(compressor, first_in_statement);
7797 function convert_spread(node) {
7798 return node instanceof AST_Spread ? make_node(AST_Array, node, {
7802 def(AST_Node, return_this);
7803 def(AST_Accessor, return_null);
7804 def(AST_Array, function(compressor, first_in_statement) {
7805 var values = trim(this.elements, compressor, first_in_statement, array_spread);
7806 if (!values) return null;
7807 if (values === this.elements && all(values, function(node) {
7808 return node instanceof AST_Spread;
7810 return make_sequence(this, values.map(convert_spread));
7812 def(AST_Assign, function(compressor) {
7813 var left = this.left;
7814 if (left instanceof AST_PropAccess) {
7815 var expr = left.expression;
7816 if (expr.may_throw_on_access(compressor, true)) return this;
7817 if (compressor.has_directive("use strict") && expr.is_constant()) return this;
7819 if (left.has_side_effects(compressor)) return this;
7820 if (lazy_op[this.operator.slice(0, -1)]) return this;
7821 this.write_only = true;
7822 if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this;
7823 return this.right.drop_side_effect_free(compressor);
7825 def(AST_Await, function(compressor) {
7826 if (!compressor.option("awaits")) return this;
7827 var exp = this.expression;
7828 if (!is_primitive(compressor, exp)) return this;
7829 var node = this.clone();
7830 node.expression = exp.drop_side_effect_free(compressor) || make_node(AST_Number, this, { value: 0 });
7833 def(AST_Binary, function(compressor, first_in_statement) {
7834 var left = this.left;
7835 var right = this.right;
7836 var op = this.operator;
7837 if (op == "in" && !is_object(right)) {
7838 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7839 if (lhs === left) return this;
7840 var node = this.clone();
7841 node.left = lhs || make_node(AST_Number, left, { value: 0 });
7844 var rhs = right.drop_side_effect_free(compressor, first_in_statement);
7845 if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement);
7846 if (lazy_op[op] && rhs.has_side_effects(compressor)) {
7848 if (rhs !== right) {
7849 node = node.clone();
7850 node.right = rhs.drop_side_effect_free(compressor);
7852 if (op == "??") return node;
7853 var negated = make_node(AST_Binary, this, {
7854 operator: op == "&&" ? "||" : "&&",
7855 left: left.negate(compressor, first_in_statement),
7858 return first_in_statement ? best_of_statement(node, negated) : best_of_expression(node, negated);
7860 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7861 if (!lhs) return rhs;
7862 rhs = rhs.drop_side_effect_free(compressor);
7863 if (!rhs) return lhs;
7864 return make_sequence(this, [ lhs, rhs ]);
7866 function drop_returns(compressor, exp) {
7867 var arrow = is_arrow(exp);
7868 var async = is_async(exp);
7869 var drop_body = false;
7870 if (arrow && compressor.option("arrows")) {
7873 } else if (!async || is_primitive(compressor, exp.value)) {
7874 exp.value = exp.value.drop_side_effect_free(compressor);
7876 } else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
7878 var def = exp.name.definition();
7879 drop_body = def.references.length == def.replaced;
7885 exp.process_expression(false, function(node) {
7886 var value = node.value;
7888 if (async && !is_primitive(compressor, value)) return node;
7889 value = value.drop_side_effect_free(compressor, true);
7891 if (!value) return make_node(AST_EmptyStatement, node);
7892 return make_node(AST_SimpleStatement, node, { body: value });
7894 scan_local_returns(exp, function(node) {
7895 var value = node.value;
7897 if (async && !is_primitive(compressor, value)) return;
7898 node.value = value.drop_side_effect_free(compressor);
7902 if (async && compressor.option("awaits")) {
7903 if (drop_body) exp.process_expression("awaits", function(node) {
7904 var body = node.body;
7905 if (body instanceof AST_Await) {
7906 if (is_primitive(compressor, body.expression)) {
7907 body = body.expression.drop_side_effect_free(compressor, true);
7908 if (!body) return make_node(AST_EmptyStatement, node);
7911 } else if (body instanceof AST_Sequence) {
7912 var exprs = body.expressions;
7913 for (var i = exprs.length; --i >= 0;) {
7914 var tail = exprs[i];
7915 if (!(tail instanceof AST_Await)) break;
7916 if (!is_primitive(compressor, tail.expression)) break;
7917 if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
7921 return make_node(AST_EmptyStatement, node);
7923 node.body = exprs[0];
7926 exprs.length = i + 1;
7932 var abort = !drop_body && exp.name || arrow && exp.value && !is_primitive(compressor, exp.value);
7933 var tw = new TreeWalker(function(node) {
7934 if (abort) return true;
7935 if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
7936 if (node instanceof AST_Await) return abort = true;
7937 if (node instanceof AST_ForAwaitOf) return abort = true;
7938 if (node instanceof AST_Return) {
7939 if (node.value && !is_primitive(compressor, node.value)) return abort = true;
7942 if (node instanceof AST_Scope && node !== exp) return true;
7948 case AST_AsyncArrow:
7951 case AST_AsyncFunction:
7952 ctor = AST_Function;
7954 case AST_AsyncGeneratorFunction:
7955 ctor = AST_GeneratorFunction;
7958 return make_node(ctor, exp, exp);
7961 return drop_body && exp.clone();
7963 def(AST_Call, function(compressor, first_in_statement) {
7965 if (self.is_expr_pure(compressor)) {
7966 if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
7967 var args = trim(self.args, compressor, first_in_statement, array_spread);
7968 return args && make_sequence(self, args.map(convert_spread));
7970 var exp = self.expression;
7971 if (self.is_call_pure(compressor)) {
7972 var exprs = self.args.slice();
7973 exprs.unshift(exp.expression);
7974 exprs = trim(exprs, compressor, first_in_statement, array_spread);
7975 return exprs && make_sequence(self, exprs.map(convert_spread));
7977 if (compressor.option("yields") && is_generator(exp)) {
7978 var call = self.clone();
7979 call.expression = make_node(AST_Function, exp, exp);
7980 call.expression.body = [];
7981 var opt = call.transform(compressor);
7982 if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
7984 var dropped = drop_returns(compressor, exp);
7986 // always shallow clone to ensure stripping of negated IIFEs
7987 self = self.clone();
7988 self.expression = dropped;
7989 // avoid extraneous traversal
7990 if (exp._squeezed) self.expression._squeezed = true;
7992 if (self instanceof AST_New) {
7994 if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
7995 if (fn instanceof AST_Lambda) {
7996 if (assign_this_only(fn, compressor)) {
7997 var exprs = self.args.slice();
7999 exprs = trim(exprs, compressor, first_in_statement, array_spread);
8000 return exprs && make_sequence(self, exprs.map(convert_spread));
8002 if (!fn.contains_this()) self = make_node(AST_Call, self, self);
8005 self.call_only = true;
8008 function assign_this_only(fn, compressor) {
8010 var result = all(fn.body, function(stat) {
8011 return !stat.has_side_effects(compressor);
8012 }) && all(fn.argnames, function(argname) {
8013 return !argname.match_symbol(return_false);
8014 }) && !(fn.rest && fn.rest.match_symbol(return_false));
8018 function drop_class(self, compressor, first_in_statement) {
8019 var exprs = [], values = [];
8020 var props = self.properties;
8021 for (var i = 0; i < props.length; i++) {
8022 var prop = props[i];
8023 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8024 if (prop.static && prop.value
8025 && prop instanceof AST_ClassField
8026 && prop.value.has_side_effects(compressor)) {
8027 if (prop.value.contains_this()) return self;
8028 values.push(prop.value);
8031 var base = self.extends;
8033 if (base instanceof AST_SymbolRef) base = base.fixed_value();
8034 base = !safe_for_extends(base);
8035 if (!base) exprs.unshift(self.extends);
8037 exprs = trim(exprs, compressor, first_in_statement);
8038 if (exprs) first_in_statement = false;
8039 values = trim(values, compressor, first_in_statement);
8041 if (!base && !values) return null;
8045 var node = to_class_expr(self, true);
8046 node.properties = [];
8047 if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, {
8048 key: make_sequence(self, exprs),
8049 value: make_node(AST_Function, self, {
8056 if (values) exprs.push(make_node(AST_Call, self, {
8057 expression: make_node(AST_Arrow, self, {
8060 value: make_sequence(self, values),
8061 }).init_vars(self.parent_scope),
8064 return make_sequence(self, exprs);
8066 def(AST_ClassExpression, function(compressor, first_in_statement) {
8068 var name = self.name;
8069 if (name && name.fixed_value() !== self && name.definition().references.length > 0) return self;
8070 return drop_class(self, compressor, first_in_statement);
8072 def(AST_Conditional, function(compressor) {
8073 var consequent = this.consequent.drop_side_effect_free(compressor);
8074 var alternative = this.alternative.drop_side_effect_free(compressor);
8075 if (consequent === this.consequent && alternative === this.alternative) return this;
8077 if (compressor.option("ie")) {
8079 if (consequent instanceof AST_Function) {
8080 exprs.push(consequent);
8083 if (alternative instanceof AST_Function) {
8084 exprs.push(alternative);
8090 node = alternative ? make_node(AST_Binary, this, {
8092 left: this.condition,
8094 }) : this.condition.drop_side_effect_free(compressor);
8095 } else if (!alternative) {
8096 node = make_node(AST_Binary, this, {
8098 left: this.condition,
8102 node = this.clone();
8103 node.consequent = consequent;
8104 node.alternative = alternative;
8106 if (!compressor.option("ie")) return node;
8107 if (node) exprs.push(node);
8108 return exprs.length == 0 ? null : make_sequence(this, exprs);
8110 def(AST_Constant, return_null);
8111 def(AST_DefClass, function(compressor, first_in_statement) {
8112 return drop_class(this, compressor, first_in_statement);
8114 def(AST_Dot, function(compressor, first_in_statement) {
8115 var expr = this.expression;
8116 if (!this.optional && expr.may_throw_on_access(compressor)) return this;
8117 return expr.drop_side_effect_free(compressor, first_in_statement);
8119 def(AST_Function, function(compressor) {
8120 return fn_name_unused(this, compressor) ? null : this;
8122 def(AST_LambdaExpression, return_null);
8123 def(AST_Object, function(compressor, first_in_statement) {
8125 this.properties.forEach(function(prop) {
8126 if (prop instanceof AST_Spread) {
8129 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8130 exprs.push(prop.value);
8133 var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
8134 var exp = node.expression;
8135 return spread_side_effects(exp) ? node : exp.drop_side_effect_free(compressor, first_in_statement);
8137 if (!values) return null;
8138 if (values === exprs && !all(values, function(node) {
8139 return !(node instanceof AST_Spread);
8141 return make_sequence(this, values.map(function(node) {
8142 return node instanceof AST_Spread ? make_node(AST_Object, node, {
8143 properties: [ node ],
8147 def(AST_ObjectIdentity, return_null);
8148 def(AST_Sequence, function(compressor, first_in_statement) {
8149 var expressions = trim(this.expressions, compressor, first_in_statement);
8150 if (!expressions) return null;
8151 var end = expressions.length - 1;
8152 var last = expressions[end];
8153 if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) {
8154 expressions = expressions.slice(0, -1);
8156 last.expression = expressions[end];
8157 expressions[end] = last;
8159 var assign, cond, lhs;
8160 if (compressor.option("conditionals")
8162 && (assign = expressions[end - 1]) instanceof AST_Assign
8163 && assign.operator == "="
8164 && (lhs = assign.left) instanceof AST_SymbolRef
8165 && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) {
8166 assign = assign.clone();
8167 assign.right = cond;
8168 expressions = expressions.slice(0, -2);
8169 expressions.push(assign.drop_side_effect_free(compressor, first_in_statement));
8171 return expressions === this.expressions ? this : make_sequence(this, expressions);
8173 def(AST_Sub, function(compressor, first_in_statement) {
8174 var expr = this.expression;
8175 var prop = this.property;
8176 if (expr.may_throw_on_access(compressor)) {
8177 if (!this.optional) return this;
8178 if (prop.has_side_effects(compressor)) {
8179 prop = prop.drop_side_effect_free(compressor);
8180 if (!prop) return expr.drop_side_effect_free(compressor, first_in_statement);
8181 var node = this.clone();
8182 node.property = prop;
8186 expr = expr.drop_side_effect_free(compressor, first_in_statement);
8187 if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement);
8188 prop = prop.drop_side_effect_free(compressor);
8189 if (!prop) return expr;
8190 return make_sequence(this, [ expr, prop ]);
8192 def(AST_SymbolRef, function(compressor) {
8193 return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
8195 def(AST_Template, function(compressor, first_in_statement) {
8197 if (self.is_expr_pure(compressor)) {
8198 var expressions = self.expressions;
8199 if (expressions.length == 0) return null;
8200 return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
8203 var dropped = drop_returns(compressor, tag);
8205 // always shallow clone to signal internal changes
8206 self = self.clone();
8208 // avoid extraneous traversal
8209 if (tag._squeezed) self.tag._squeezed = true;
8213 def(AST_Unary, function(compressor, first_in_statement) {
8214 var exp = this.expression;
8215 if (unary_side_effects[this.operator]) {
8216 this.write_only = !exp.has_side_effects(compressor);
8219 if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) {
8222 var node = exp.drop_side_effect_free(compressor, first_in_statement);
8223 if (first_in_statement && node && is_iife_call(node)) {
8224 if (node === exp && this.operator == "!") return this;
8225 return node.negate(compressor, first_in_statement);
8229 })(function(node, func) {
8230 node.DEFMETHOD("drop_side_effect_free", func);
8233 OPT(AST_SimpleStatement, function(self, compressor) {
8234 if (compressor.option("side_effects")) {
8235 var body = self.body;
8236 var node = body.drop_side_effect_free(compressor, true);
8238 AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
8239 return make_node(AST_EmptyStatement, self);
8241 if (node !== body) {
8242 return make_node(AST_SimpleStatement, self, { body: node });
8248 OPT(AST_While, function(self, compressor) {
8249 return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
8252 function has_loop_control(loop, parent, type) {
8253 if (!type) type = AST_LoopControl;
8255 var tw = new TreeWalker(function(node) {
8256 if (found || node instanceof AST_Scope) return true;
8257 if (node instanceof type && tw.loopcontrol_target(node) === loop) {
8258 return found = true;
8261 if (parent instanceof AST_LabeledStatement) tw.push(parent);
8267 OPT(AST_Do, function(self, compressor) {
8268 if (!compressor.option("loops")) return self;
8269 var cond = fuzzy_eval(compressor, self.condition);
8270 if (!(cond instanceof AST_Node)) {
8271 if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, {
8272 body: make_node(AST_BlockStatement, self.body, {
8275 make_node(AST_SimpleStatement, self.condition, {
8276 body: self.condition
8280 }).optimize(compressor);
8281 if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, {
8284 make_node(AST_SimpleStatement, self.condition, {
8285 body: self.condition
8288 }).optimize(compressor);
8290 if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
8291 var body = self.body.body;
8292 for (var i = body.length; --i >= 0;) {
8294 if (stat instanceof AST_If
8295 && !stat.alternative
8296 && stat.body instanceof AST_Break
8297 && compressor.loopcontrol_target(stat.body) === self) {
8298 if (has_block_scope_refs(stat.condition)) break;
8299 self.condition = make_node(AST_Binary, self, {
8301 left: stat.condition.negate(compressor),
8302 right: self.condition,
8305 } else if (stat instanceof AST_SimpleStatement) {
8306 if (has_block_scope_refs(stat.body)) break;
8307 self.condition = make_sequence(self, [
8312 } else if (!is_declaration(stat, true)) {
8316 self.body = trim_block(self.body, compressor.parent());
8318 if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
8319 if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
8320 condition: make_sequence(self.condition, [
8324 body: make_node(AST_EmptyStatement, self)
8325 }).optimize(compressor);
8328 function has_block_scope_refs(node) {
8330 node.walk(new TreeWalker(function(node) {
8331 if (found) return true;
8332 if (node instanceof AST_SymbolRef) {
8333 if (!member(node.definition(), self.enclosed)) found = true;
8341 function if_break_in_loop(self, compressor) {
8342 var first = first_statement(self.body);
8343 if (compressor.option("dead_code")
8344 && (first instanceof AST_Break
8345 || first instanceof AST_Continue && external_target(first)
8346 || first instanceof AST_Exit)) {
8348 if (is_statement(self.init)) {
8349 body.push(self.init);
8350 } else if (self.init) {
8351 body.push(make_node(AST_SimpleStatement, self.init, {
8355 var retain = external_target(first) || first instanceof AST_Exit;
8356 if (self.condition && retain) {
8357 body.push(make_node(AST_If, self, {
8358 condition: self.condition,
8362 } else if (self.condition) {
8363 body.push(make_node(AST_SimpleStatement, self.condition, {
8364 body: self.condition
8366 } else if (retain) {
8369 extract_declarations_from_unreachable_code(compressor, self.body, body);
8370 return make_node(AST_BlockStatement, self, {
8374 if (first instanceof AST_If) {
8375 var ab = first_statement(first.body);
8376 if (ab instanceof AST_Break && !external_target(ab)) {
8377 if (self.condition) {
8378 self.condition = make_node(AST_Binary, self.condition, {
8379 left: self.condition,
8381 right: first.condition.negate(compressor),
8384 self.condition = first.condition.negate(compressor);
8386 var body = as_statement_array(first.alternative);
8387 extract_declarations_from_unreachable_code(compressor, first.body, body);
8388 return drop_it(body);
8390 ab = first_statement(first.alternative);
8391 if (ab instanceof AST_Break && !external_target(ab)) {
8392 if (self.condition) {
8393 self.condition = make_node(AST_Binary, self.condition, {
8394 left: self.condition,
8396 right: first.condition,
8399 self.condition = first.condition;
8401 var body = as_statement_array(first.body);
8402 extract_declarations_from_unreachable_code(compressor, first.alternative, body);
8403 return drop_it(body);
8408 function first_statement(body) {
8409 return body instanceof AST_BlockStatement ? body.body[0] : body;
8412 function external_target(node) {
8413 return compressor.loopcontrol_target(node) !== compressor.self();
8416 function drop_it(rest) {
8417 if (self.body instanceof AST_BlockStatement) {
8418 self.body = self.body.clone();
8419 self.body.body = rest.concat(self.body.body.slice(1));
8420 self.body = self.body.transform(compressor);
8422 self.body = make_node(AST_BlockStatement, self.body, {
8424 }).transform(compressor);
8426 return if_break_in_loop(self, compressor);
8430 OPT(AST_For, function(self, compressor) {
8431 if (!compressor.option("loops")) return self;
8432 if (compressor.option("side_effects")) {
8433 if (self.init) self.init = self.init.drop_side_effect_free(compressor);
8434 if (self.step) self.step = self.step.drop_side_effect_free(compressor);
8436 if (self.condition) {
8437 var cond = fuzzy_eval(compressor, self.condition);
8439 if (compressor.option("dead_code")) {
8441 if (is_statement(self.init)) {
8442 body.push(self.init);
8443 } else if (self.init) {
8444 body.push(make_node(AST_SimpleStatement, self.init, { body: self.init }));
8446 body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition }));
8447 extract_declarations_from_unreachable_code(compressor, self.body, body);
8448 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8450 } else if (!(cond instanceof AST_Node)) {
8451 self.body = make_node(AST_BlockStatement, self.body, {
8453 make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8457 self.condition = null;
8460 return if_break_in_loop(self, compressor);
8463 OPT(AST_ForEnumeration, function(self, compressor) {
8464 if (compressor.option("varify") && is_lexical_definition(self.init)) {
8465 var name = self.init.definitions[0].name;
8466 if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
8467 && !name.match_symbol(function(node) {
8468 if (node instanceof AST_SymbolDeclaration) {
8469 var def = node.definition();
8470 return !same_scope(def) || may_overlap(compressor, def);
8473 self.init = to_var(self.init);
8479 function mark_locally_defined(condition, consequent, alternative) {
8480 if (!(condition instanceof AST_Binary)) return;
8481 if (!(condition.left instanceof AST_String)) {
8482 switch (condition.operator) {
8484 mark_locally_defined(condition.left, consequent);
8485 mark_locally_defined(condition.right, consequent);
8488 mark_locally_defined(negate(condition.left), alternative);
8489 mark_locally_defined(negate(condition.right), alternative);
8494 if (!(condition.right instanceof AST_UnaryPrefix)) return;
8495 if (condition.right.operator != "typeof") return;
8496 var sym = condition.right.expression;
8497 if (!is_undeclared_ref(sym)) return;
8499 var undef = condition.left.value == "undefined";
8500 switch (condition.operator) {
8502 body = undef ? alternative : consequent;
8505 body = undef ? consequent : alternative;
8511 var def = sym.definition();
8512 var tw = new TreeWalker(function(node) {
8513 if (node instanceof AST_Scope) {
8514 var parent = tw.parent();
8515 if (parent instanceof AST_Call && parent.expression === node) return;
8518 if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
8522 function negate(node) {
8523 if (!(node instanceof AST_Binary)) return;
8524 switch (node.operator) {
8526 node = node.clone();
8527 node.operator = "!=";
8530 node = node.clone();
8531 node.operator = "==";
8537 function fuzzy_eval(compressor, node, nullish) {
8538 if (node.truthy) return true;
8539 if (node.falsy && !nullish) return false;
8540 if (node.is_truthy()) return true;
8541 return node.evaluate(compressor, true);
8544 function mark_duplicate_condition(compressor, node) {
8547 var negated = false;
8548 var parent = compressor.self();
8549 if (!is_statement(parent)) while (true) {
8551 parent = compressor.parent(level++);
8552 if (parent instanceof AST_Binary) {
8553 var op = parent.operator;
8554 if (!lazy_op[op]) return;
8555 var left = parent.left;
8556 if (left === child) continue;
8557 if (match(left)) switch (op) {
8559 node[negated ? "falsy" : "truthy"] = true;
8563 node[negated ? "truthy" : "falsy"] = true;
8566 } else if (parent instanceof AST_Conditional) {
8567 var cond = parent.condition;
8568 if (cond === child) continue;
8569 if (match(cond)) switch (child) {
8570 case parent.consequent:
8571 node[negated ? "falsy" : "truthy"] = true;
8573 case parent.alternative:
8574 node[negated ? "truthy" : "falsy"] = true;
8577 } else if (parent instanceof AST_Exit) {
8579 } else if (parent instanceof AST_If) {
8581 } else if (parent instanceof AST_Sequence) {
8582 if (parent.expressions[0] === child) continue;
8583 } else if (parent instanceof AST_SimpleStatement) {
8590 parent = compressor.parent(level++);
8591 if (parent instanceof AST_BlockStatement) {
8592 if (parent.body[0] === child) continue;
8593 } else if (parent instanceof AST_If) {
8594 if (match(parent.condition)) switch (child) {
8596 node[negated ? "falsy" : "truthy"] = true;
8598 case parent.alternative:
8599 node[negated ? "truthy" : "falsy"] = true;
8606 function match(cond) {
8607 if (node.equivalent_to(cond)) return true;
8608 if (!(cond instanceof AST_UnaryPrefix)) return false;
8609 if (cond.operator != "!") return false;
8610 if (!node.equivalent_to(cond.expression)) return false;
8616 OPT(AST_If, function(self, compressor) {
8617 if (is_empty(self.alternative)) self.alternative = null;
8619 if (!compressor.option("conditionals")) return self;
8620 if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) {
8621 mark_duplicate_condition(compressor, self.condition);
8623 // if condition can be statically determined, warn and drop
8624 // one of the blocks. note, statically determined implies
8625 // “has no side effects”; also it doesn't work for cases like
8626 // `x && true`, though it probably should.
8627 if (compressor.option("dead_code")) {
8628 var cond = fuzzy_eval(compressor, self.condition);
8630 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
8631 var body = [ make_node(AST_SimpleStatement, self.condition, { body: self.condition }) ];
8632 extract_declarations_from_unreachable_code(compressor, self.body, body);
8633 if (self.alternative) body.push(self.alternative);
8634 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8635 } else if (!(cond instanceof AST_Node)) {
8636 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
8638 make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8641 if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body);
8642 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8645 var negated = self.condition.negate(compressor);
8646 var self_condition_length = self.condition.print_to_string().length;
8647 var negated_length = negated.print_to_string().length;
8648 var negated_is_best = negated_length < self_condition_length;
8649 if (self.alternative && negated_is_best) {
8650 negated_is_best = false; // because we already do the switch here.
8651 // no need to swap values of self_condition_length and negated_length
8652 // here because they are only used in an equality comparison later on.
8653 self.condition = negated;
8654 var tmp = self.body;
8655 self.body = self.alternative || make_node(AST_EmptyStatement, self);
8656 self.alternative = tmp;
8658 var body = [], var_defs = [], refs = [];
8659 var body_exprs = sequencesize(self.body, body, var_defs, refs);
8660 var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
8661 if (body_exprs && alt_exprs) {
8662 if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
8663 if (body_exprs.length == 0) {
8664 body.push(make_node(AST_SimpleStatement, self.condition, {
8665 body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
8667 left : self.condition,
8668 right : make_sequence(self.alternative, alt_exprs)
8669 }).transform(compressor) : self.condition.clone()
8670 }).optimize(compressor));
8671 } else if (alt_exprs.length == 0) {
8672 if (self_condition_length === negated_length && !negated_is_best
8673 && self.condition instanceof AST_Binary && self.condition.operator == "||") {
8674 // although the code length of self.condition and negated are the same,
8675 // negated does not require additional surrounding parentheses.
8676 // see https://github.com/mishoo/UglifyJS/issues/979
8677 negated_is_best = true;
8679 body.push(make_node(AST_SimpleStatement, self, {
8680 body: make_node(AST_Binary, self, {
8681 operator : negated_is_best ? "||" : "&&",
8682 left : negated_is_best ? negated : self.condition,
8683 right : make_sequence(self.body, body_exprs)
8684 }).transform(compressor)
8685 }).optimize(compressor));
8687 body.push(make_node(AST_SimpleStatement, self, {
8688 body: make_node(AST_Conditional, self, {
8689 condition : self.condition,
8690 consequent : make_sequence(self.body, body_exprs),
8691 alternative : make_sequence(self.alternative, alt_exprs)
8693 }).optimize(compressor));
8695 refs.forEach(function(ref) {
8696 ref.definition().references.push(ref);
8698 return make_node(AST_BlockStatement, self, {
8700 }).optimize(compressor);
8702 if (is_empty(self.body)) {
8703 self = make_node(AST_If, self, {
8705 body: self.alternative,
8709 if (self.body instanceof AST_Exit
8710 && self.alternative instanceof AST_Exit
8711 && self.body.TYPE == self.alternative.TYPE) {
8712 var exit = make_node(self.body.CTOR, self, {
8713 value: make_node(AST_Conditional, self, {
8714 condition : self.condition,
8715 consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
8716 alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
8719 if (exit instanceof AST_Return) {
8720 exit.in_bool = self.body.in_bool || self.alternative.in_bool;
8724 if (self.body instanceof AST_If
8725 && !self.body.alternative
8726 && !self.alternative) {
8727 self = make_node(AST_If, self, {
8728 condition: make_node(AST_Binary, self.condition, {
8730 left: self.condition,
8731 right: self.body.condition
8733 body: self.body.body,
8737 if (aborts(self.body)) {
8738 if (self.alternative) {
8739 var alt = self.alternative;
8740 self.alternative = null;
8741 return make_node(AST_BlockStatement, self, {
8743 }).optimize(compressor);
8746 if (aborts(self.alternative)) {
8747 var body = self.body;
8748 self.body = self.alternative;
8749 self.condition = negated_is_best ? negated : self.condition.negate(compressor);
8750 self.alternative = null;
8751 return make_node(AST_BlockStatement, self, {
8752 body: [ self, body ]
8753 }).optimize(compressor);
8755 if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
8758 function sequencesize(stat, defuns, var_defs, refs) {
8759 if (stat == null) return [];
8760 if (stat instanceof AST_BlockStatement) {
8762 for (var i = 0; i < stat.body.length; i++) {
8763 var line = stat.body[i];
8764 if (line instanceof AST_LambdaDefinition) {
8766 } else if (line instanceof AST_EmptyStatement) {
8768 } else if (line instanceof AST_SimpleStatement) {
8769 if (!compressor.option("sequences") && exprs.length > 0) return;
8770 exprs.push(line.body);
8771 } else if (line instanceof AST_Var) {
8772 if (!compressor.option("sequences") && exprs.length > 0) return;
8773 line.remove_initializers(compressor, var_defs);
8774 line.definitions.forEach(process_var_def);
8781 if (stat instanceof AST_LambdaDefinition) {
8785 if (stat instanceof AST_EmptyStatement) return [];
8786 if (stat instanceof AST_SimpleStatement) return [ stat.body ];
8787 if (stat instanceof AST_Var) {
8789 stat.remove_initializers(compressor, var_defs);
8790 stat.definitions.forEach(process_var_def);
8794 function process_var_def(var_def) {
8795 if (!var_def.value) return;
8796 exprs.push(make_node(AST_Assign, var_def, {
8798 left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) {
8801 right: var_def.value
8807 OPT(AST_Switch, function(self, compressor) {
8808 if (!compressor.option("switches")) return self;
8809 if (!compressor.option("dead_code")) return self;
8815 var side_effects = [];
8816 for (var i = 0, len = self.body.length; i < len; i++) {
8817 branch = self.body[i];
8818 if (branch instanceof AST_Default) {
8819 var prev = body[body.length - 1];
8820 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
8821 eliminate_branch(branch, prev);
8824 default_branch = branch;
8827 var exp = branch.expression;
8828 var equals = make_node(AST_Binary, self, {
8830 left: self.expression,
8832 }).evaluate(compressor, true);
8834 if (exp.has_side_effects(compressor)) side_effects.push(exp);
8835 eliminate_branch(branch, body[body.length - 1]);
8838 if (!(equals instanceof AST_Node)) {
8839 if (default_branch) {
8840 var default_index = body.indexOf(default_branch);
8841 body.splice(default_index, 1);
8842 eliminate_branch(default_branch, body[default_index - 1]);
8843 default_branch = null;
8845 if (exp.has_side_effects(compressor)) {
8846 exact_match = branch;
8848 default_branch = branch = make_node(AST_Default, branch, branch);
8850 while (++i < len) eliminate_branch(self.body[i], branch);
8853 if (i + 1 >= len || aborts(branch)) {
8854 var prev = body[body.length - 1];
8855 var statements = branch.body;
8856 if (aborts(prev)) switch (prev.body.length - statements.length) {
8858 var stat = prev.body[prev.body.length - 1];
8859 if (!is_break(stat, compressor)) break;
8860 statements = statements.concat(stat);
8862 var prev_block = make_node(AST_BlockStatement, prev, prev);
8863 var next_block = make_node(AST_BlockStatement, branch, { body: statements });
8864 if (prev_block.equivalent_to(next_block)) prev.body = [];
8867 if (side_effects.length) {
8868 if (branch instanceof AST_Default) {
8869 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8871 side_effects.push(branch.expression);
8872 branch.expression = make_sequence(self, side_effects);
8878 if (side_effects.length && !exact_match) {
8879 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8881 while (branch = body[body.length - 1]) {
8882 var stat = branch.body[branch.body.length - 1];
8883 if (is_break(stat, compressor)) branch.body.pop();
8884 if (branch === default_branch) {
8885 if (!has_declarations_only(branch)) break;
8886 } else if (branch.expression.has_side_effects(compressor)) {
8888 } else if (default_branch) {
8889 if (!has_declarations_only(default_branch)) break;
8890 if (body[body.length - 2] !== default_branch) break;
8891 default_branch.body = default_branch.body.concat(branch.body);
8893 } else if (!has_declarations_only(branch)) break;
8894 eliminate_branch(branch);
8895 if (body.pop() === default_branch) default_branch = null;
8898 decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
8899 if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, {
8900 body: make_sequence(self, side_effects),
8902 return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor);
8904 if (branch === default_branch) while (branch = body[body.length - 2]) {
8905 if (branch instanceof AST_Default) break;
8906 if (!has_declarations_only(branch)) break;
8907 var exp = branch.expression;
8908 if (exp.has_side_effects(compressor)) {
8909 var prev = body[body.length - 3];
8910 if (prev && !aborts(prev)) break;
8911 default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp }));
8913 eliminate_branch(branch);
8916 body[0].body = decl.concat(body[0].body);
8918 if (compressor.option("conditionals")) switch (body.length) {
8920 if (!no_break(body[0])) break;
8921 var exp = body[0].expression;
8922 var statements = body[0].body.slice();
8923 if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
8924 condition: make_node(AST_Binary, self, {
8926 left: self.expression,
8929 body: make_node(AST_BlockStatement, self, {
8933 }).optimize(compressor);
8934 if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
8937 statements.unshift(make_node(AST_SimpleStatement, self.expression, {
8938 body:self.expression,
8940 return make_node(AST_BlockStatement, self, {
8942 }).optimize(compressor);
8944 if (!member(default_branch, body) || !no_break(body[1])) break;
8945 var statements = body[0].body.slice();
8946 var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
8947 if (exclusive) statements.pop();
8948 if (!all(statements, no_break)) break;
8949 var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1], body[1]);
8950 var node = make_node(AST_If, self, {
8951 condition: make_node(AST_Binary, self, body[0] === default_branch ? {
8953 left: self.expression,
8954 right: body[1].expression,
8957 left: self.expression,
8958 right: body[0].expression,
8960 body: make_node(AST_BlockStatement, body[0], {
8963 alternative: exclusive && alternative || null,
8965 if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, {
8966 body: [ node, alternative ],
8968 return node.optimize(compressor);
8972 function is_break(node, tw) {
8973 return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
8976 function no_break(node) {
8978 var tw = new TreeWalker(function(node) {
8980 || node instanceof AST_Lambda
8981 || node instanceof AST_SimpleStatement) return true;
8982 if (is_break(node, tw)) found = true;
8989 function eliminate_branch(branch, prev) {
8990 if (prev && !aborts(prev)) {
8991 prev.body = prev.body.concat(branch.body);
8993 extract_declarations_from_unreachable_code(compressor, branch, decl);
8998 OPT(AST_Try, function(self, compressor) {
8999 self.body = tighten_body(self.body, compressor);
9000 if (compressor.option("dead_code")) {
9001 if (has_declarations_only(self)
9002 && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
9003 return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
9007 extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
9008 body.forEach(function(stat) {
9009 if (!(stat instanceof AST_Var)) return;
9010 stat.definitions.forEach(function(var_def) {
9011 var def = var_def.name.definition().redefined();
9013 var_def.name = var_def.name.clone();
9014 var_def.name.thedef = def;
9018 body.unshift(make_node(AST_BlockStatement, self, self).optimize(compressor));
9019 if (self.bfinally) {
9020 body.push(make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor));
9022 return make_node(AST_BlockStatement, self, {
9024 }).optimize(compressor);
9026 if (self.bfinally && has_declarations_only(self.bfinally)) {
9027 var body = make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor);
9028 body = self.body.concat(body);
9029 if (!self.bcatch) return make_node(AST_BlockStatement, self, {
9031 }).optimize(compressor);
9033 self.bfinally = null;
9039 function remove_initializers(make_value) {
9040 return function(compressor, defns) {
9041 var dropped = false;
9042 this.definitions.forEach(function(defn) {
9043 if (defn.value) dropped = true;
9044 defn.name.match_symbol(function(node) {
9045 if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, {
9047 value: make_value(compressor, node)
9055 AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) {
9056 return make_node(AST_Undefined, node).optimize(compressor);
9058 AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9059 AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9061 AST_Definitions.DEFMETHOD("to_assignments", function() {
9062 var assignments = this.definitions.reduce(function(a, defn) {
9063 var def = defn.name.definition();
9064 var value = defn.value;
9066 if (value instanceof AST_Sequence) value = value.clone();
9067 var name = make_node(AST_SymbolRef, defn.name, defn.name);
9068 var assign = make_node(AST_Assign, defn, {
9074 var fixed = function() {
9075 return assign.right;
9077 fixed.assigns = [ assign ];
9078 fixed.direct_access = def.direct_access;
9079 fixed.escaped = def.escaped;
9081 def.references.forEach(function(ref) {
9082 var assigns = ref.fixed && ref.fixed.assigns;
9083 if (assigns && assigns[0] === defn) assigns[0] = assign;
9085 def.references.push(name);
9088 def.single_use = false;
9091 if (assignments.length == 0) return null;
9092 return make_sequence(this, assignments);
9095 function is_safe_lexical(def) {
9096 return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
9099 function may_overlap(compressor, def) {
9100 if (compressor.exposed(def)) return true;
9101 var scope = def.scope.resolve();
9102 for (var s = def.scope; s !== scope;) {
9104 if (s.var_names()[def.name]) return true;
9108 function to_var(stat) {
9109 return make_node(AST_Var, stat, {
9110 definitions: stat.definitions.map(function(defn) {
9111 return make_node(AST_VarDef, defn, {
9112 name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
9113 var def = name.definition();
9114 def.orig[def.orig.indexOf(node)] = name;
9115 var scope = def.scope.resolve();
9116 if (def.scope === scope) return;
9118 scope.variables.set(def.name, def);
9119 scope.enclosed.push(def);
9120 scope.var_names()[def.name] = true;
9128 function can_varify(compressor, sym) {
9129 if (!sym.fixed_value()) return false;
9130 var def = sym.definition();
9131 return is_safe_lexical(def) && same_scope(def) && !may_overlap(compressor, def);
9134 function varify(self, compressor) {
9135 return compressor.option("varify") && all(self.definitions, function(defn) {
9136 return !defn.name.match_symbol(function(node) {
9137 if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
9139 }) ? to_var(self) : self;
9142 OPT(AST_Const, varify);
9143 OPT(AST_Let, varify);
9145 function trim_optional_chain(node, compressor) {
9146 if (!compressor.option("optional_chains")) return;
9147 if (node.terminal) do {
9148 var expr = node.expression;
9149 if (node.optional) {
9150 var ev = fuzzy_eval(compressor, expr, true);
9151 if (ev == null) return make_node(AST_UnaryPrefix, node, {
9154 }).optimize(compressor);
9155 if (!(ev instanceof AST_Node)) node.optional = false;
9158 } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal);
9161 function lift_sequence_in_expression(node, compressor) {
9162 var exp = node.expression;
9163 if (!(exp instanceof AST_Sequence)) return node;
9164 var x = exp.expressions.slice();
9165 var e = node.clone();
9166 e.expression = x.pop();
9168 return make_sequence(node, x);
9171 function drop_unused_call_args(call, compressor, fns_with_marked_args) {
9172 var exp = call.expression;
9173 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9174 if (!(fn instanceof AST_Lambda)) return;
9175 if (fn.uses_arguments) return;
9176 if (fn.pinned()) return;
9177 if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
9178 var args = call.args;
9179 if (!all(args, function(arg) {
9180 return !(arg instanceof AST_Spread);
9182 var argnames = fn.argnames;
9183 var is_iife = fn === exp && !fn.name;
9185 if (!(is_iife && compressor.option("rests"))) return;
9186 var insert = argnames.length;
9187 args = args.slice(0, insert);
9188 while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor));
9189 args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) }));
9190 argnames = argnames.concat(fn.rest);
9193 args = args.slice();
9194 argnames = argnames.slice();
9196 var pos = 0, last = 0;
9197 var drop_defaults = is_iife && compressor.option("default_values");
9198 var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
9199 if (!argname) return true;
9200 if (argname instanceof AST_DestructuredArray) {
9201 return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
9203 if (argname instanceof AST_DestructuredObject) {
9204 return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
9206 return argname.__unused;
9208 var side_effects = [];
9209 for (var i = 0; i < args.length; i++) {
9210 var argname = argnames[i];
9211 if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
9212 argnames[i] = argname = argname.name;
9214 if (!argname || "__unused" in argname) {
9215 var node = args[i].drop_side_effect_free(compressor);
9216 if (drop_fargs(argname)) {
9217 if (argname) argnames.splice(i, 1);
9219 if (node) side_effects.push(node);
9223 side_effects.push(node);
9224 args[pos++] = make_sequence(call, side_effects);
9226 } else if (argname) {
9227 if (side_effects.length) {
9228 args[pos++] = make_sequence(call, side_effects);
9231 args[pos++] = make_node(AST_Number, args[i], {
9237 } else if (drop_fargs(argname, args[i])) {
9238 var node = args[i].drop_side_effect_free(compressor);
9239 argnames.splice(i, 1);
9241 if (node) side_effects.push(node);
9245 side_effects.push(args[i]);
9246 args[pos++] = make_sequence(call, side_effects);
9251 for (; i < argnames.length; i++) {
9252 if (drop_fargs(argnames[i])) argnames.splice(i--, 1);
9254 fn.argnames = argnames;
9257 if (!side_effects.length) return;
9258 var arg = make_sequence(call, side_effects);
9259 args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, {
9265 function avoid_await_yield(parent_scope) {
9267 if (is_async(parent_scope)) avoid.push("await");
9268 if (is_generator(parent_scope)) avoid.push("yield");
9269 return avoid.length && makePredicate(avoid);
9272 OPT(AST_Call, function(self, compressor) {
9273 var exp = self.expression;
9274 var terminated = trim_optional_chain(self, compressor);
9275 if (terminated) return terminated;
9276 if (compressor.option("sequences")) {
9277 if (exp instanceof AST_PropAccess) {
9278 var seq = lift_sequence_in_expression(exp, compressor);
9280 var call = self.clone();
9281 call.expression = seq.expressions.pop();
9282 seq.expressions.push(call);
9283 return seq.optimize(compressor);
9285 } else if (!needs_unbinding(compressor, exp.tail_node())) {
9286 var seq = lift_sequence_in_expression(self, compressor);
9287 if (seq !== self) return seq.optimize(compressor);
9290 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
9291 if (compressor.option("unsafe")) {
9292 if (is_undeclared_ref(exp)) switch (exp.name) {
9294 // Array(n) ---> [ , , ... , ]
9295 if (self.args.length == 1) {
9296 var first = self.args[0];
9297 if (first instanceof AST_Number) try {
9298 var length = first.value;
9299 if (length > 6) break;
9300 var elements = Array(length);
9301 for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
9302 return make_node(AST_Array, self, { elements: elements });
9304 AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
9306 file: self.start.file,
9307 line: self.start.line,
9308 col: self.start.col,
9312 if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
9314 // Array(...) ---> [ ... ]
9315 return make_node(AST_Array, self, { elements: self.args });
9318 if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
9322 if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
9323 // String(x) ---> "" + x
9324 if (self.args.length == 1) return make_node(AST_Binary, self, {
9326 left: make_node(AST_String, self, { value: "" }),
9327 right: self.args[0],
9328 }).optimize(compressor);
9332 if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
9333 // Number(x) ---> +("" + x)
9334 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9336 expression: make_node(AST_Binary, self, {
9338 left: make_node(AST_String, self, { value: "" }),
9339 right: self.args[0],
9341 }).optimize(compressor);
9344 // Boolean() ---> false
9345 if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
9346 // Boolean(x) ---> !!x
9347 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9349 expression: make_node(AST_UnaryPrefix, self, {
9351 expression: self.args[0],
9353 }).optimize(compressor);
9356 // attempt to convert RegExp(...) to literal
9358 if (all(self.args, function(arg) {
9359 var value = arg.evaluate(compressor);
9360 params.unshift(value);
9361 return arg !== value;
9363 return best_of(compressor, self, make_node(AST_RegExp, self, {
9364 value: RegExp.apply(RegExp, params),
9367 AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
9369 file: self.start.file,
9370 line: self.start.line,
9371 col: self.start.col,
9375 } else if (exp instanceof AST_Dot) switch (exp.property) {
9377 // x.toString() ---> "" + x
9378 var expr = exp.expression;
9379 if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
9380 return make_node(AST_Binary, self, {
9382 left: make_node(AST_String, self, { value: "" }),
9384 }).optimize(compressor);
9388 if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
9389 var separator = self.args[0];
9390 // [].join() ---> ""
9391 // [].join(x) ---> (x, "")
9392 if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
9393 return separator ? make_sequence(self, [
9395 make_node(AST_String, self, { value: "" }),
9396 ]).optimize(compressor) : make_node(AST_String, self, { value: "" });
9399 separator = separator.evaluate(compressor);
9400 if (separator instanceof AST_Node) break EXIT; // not a constant
9404 for (var i = 0; i < exp.expression.elements.length; i++) {
9405 var el = exp.expression.elements[i];
9406 var value = el.evaluate(compressor);
9409 } else if (el instanceof AST_Spread) {
9412 if (consts.length > 0) {
9413 elements.push(make_node(AST_String, self, {
9414 value: consts.join(separator),
9421 if (consts.length > 0) elements.push(make_node(AST_String, self, {
9422 value: consts.join(separator),
9424 // [ x ].join() ---> "" + x
9425 // [ x ].join(".") ---> "" + x
9426 // [ 1, 2, 3 ].join() ---> "1,2,3"
9427 // [ 1, 2, 3 ].join(".") ---> "1.2.3"
9428 if (elements.length == 1) {
9429 if (elements[0].is_string(compressor)) return elements[0];
9430 return make_node(AST_Binary, elements[0], {
9432 left: make_node(AST_String, self, { value: "" }),
9436 // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
9437 if (separator == "") {
9439 if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
9440 first = elements.shift();
9442 first = make_node(AST_String, self, { value: "" });
9444 return elements.reduce(function(prev, el) {
9445 return make_node(AST_Binary, el, {
9450 }, first).optimize(compressor);
9452 // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
9453 // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
9454 // need this awkward cloning to not affect original element
9455 // best_of will decide which one to get through.
9456 var node = self.clone();
9457 node.expression = node.expression.clone();
9458 node.expression.expression = node.expression.expression.clone();
9459 node.expression.expression.elements = elements;
9460 return best_of(compressor, self, node);
9464 if (self.args.length < 2) {
9465 var node = make_node(AST_Binary, self, {
9467 left: make_node(AST_Sub, self, {
9468 expression: exp.expression,
9469 property: self.args.length ? make_node(AST_Binary, self.args[0], {
9471 left: make_node(AST_Number, self, { value: 0 }),
9472 right: self.args[0],
9473 }) : make_node(AST_Number, self, { value: 0 }),
9474 }).optimize(compressor),
9475 right: make_node(AST_String, self, { value: "" }),
9477 node.is_string = return_true;
9478 return node.optimize(compressor);
9482 if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
9483 var args = self.args[1].elements.slice();
9484 args.unshift(self.args[0]);
9485 return make_node(AST_Call, self, {
9486 expression: make_node(AST_Dot, exp, {
9487 expression: exp.expression,
9491 }).optimize(compressor);
9495 var func = exp.expression;
9496 if (func instanceof AST_SymbolRef) {
9497 func = func.fixed_value();
9499 if (func instanceof AST_Lambda && !func.contains_this()) {
9500 return (self.args.length ? make_sequence(this, [
9502 make_node(AST_Call, self, {
9503 expression: exp.expression,
9504 args: self.args.slice(1)
9506 ]) : make_node(AST_Call, self, {
9507 expression: exp.expression,
9509 })).optimize(compressor);
9514 if (compressor.option("unsafe_Function")
9515 && is_undeclared_ref(exp)
9516 && exp.name == "Function") {
9517 // new Function() ---> function(){}
9518 if (self.args.length == 0) return make_node(AST_Function, self, {
9521 }).init_vars(exp.scope);
9522 if (all(self.args, function(x) {
9523 return x instanceof AST_String;
9525 // quite a corner-case, but we can handle it:
9526 // https://github.com/mishoo/UglifyJS/issues/203
9527 // if the code argument is a constant, then we can minify it.
9529 var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
9531 }).join() + "){" + self.args[self.args.length - 1].value + "})";
9532 var ast = parse(code);
9533 var mangle = { ie: compressor.option("ie") };
9534 ast.figure_out_scope(mangle);
9535 var comp = new Compressor(compressor.options);
9536 ast = ast.transform(comp);
9537 ast.figure_out_scope(mangle);
9538 ast.compute_char_frequency(mangle);
9539 ast.mangle_names(mangle);
9541 ast.walk(new TreeWalker(function(node) {
9542 if (fun) return true;
9543 if (node instanceof AST_Lambda) {
9548 var code = OutputStream();
9549 AST_BlockStatement.prototype._codegen.call(fun, code);
9551 make_node(AST_String, self, {
9552 value: fun.argnames.map(function(arg) {
9553 return arg.print_to_string();
9556 make_node(AST_String, self.args[self.args.length - 1], {
9557 value: code.get().replace(/^\{|\}$/g, "")
9562 if (ex instanceof JS_Parse_Error) {
9563 AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
9564 AST_Node.warn(ex.toString());
9571 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9572 var parent = compressor.parent(), current = compressor.self();
9573 var is_func = fn instanceof AST_Lambda
9574 && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
9575 && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
9576 var stat = is_func && fn.first_statement();
9577 var has_default = 0, has_destructured = false;
9578 var has_spread = !all(self.args, function(arg) {
9579 return !(arg instanceof AST_Spread);
9581 var can_drop = is_func && all(fn.argnames, function(argname, index) {
9582 if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
9583 if (argname instanceof AST_DefaultValue) {
9584 if (!has_default) has_default = 1;
9585 var arg = has_default == 1 && self.args[index];
9586 if (arg && !is_undefined(arg)) has_default = 2;
9587 if (has_arg_refs(argname.value)) return false;
9588 argname = argname.name;
9590 if (argname instanceof AST_Destructured) {
9591 has_destructured = true;
9592 if (has_arg_refs(argname)) return false;
9595 }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn.rest));
9596 var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
9597 if (can_inline && stat instanceof AST_Return) {
9598 var value = stat.value;
9601 && (!value || value.is_constant_expression())
9602 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9603 return make_sequence(self, convert_args(value)).optimize(compressor);
9607 var def, value, var_assigned = false;
9609 && !fn.uses_arguments
9611 && !(fn.name && fn instanceof AST_LambdaExpression)
9612 && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
9613 && fn.is_constant_expression(find_scope(compressor)))
9615 && (value = can_flatten_body(stat))
9616 && !fn.contains_this()) {
9617 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
9618 if (can_substitute_directly()) {
9619 var args = self.args.slice();
9621 var retValue = value.clone(true).transform(new TreeTransformer(function(node) {
9622 if (node instanceof AST_SymbolRef) {
9623 var def = node.definition();
9624 if (fn.variables.get(node.name) !== def) {
9628 var index = resolve_index(def);
9629 var arg = args[index];
9630 if (!arg) return make_node(AST_Undefined, self);
9632 var parent = this.parent();
9633 return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
9636 var save_inlined = fn.inlined;
9637 if (exp !== fn) fn.inlined = true;
9639 args.forEach(function(arg) {
9641 arg = arg.clone(true);
9642 arg.walk(new TreeWalker(function(node) {
9643 if (node instanceof AST_SymbolRef) refs.push(node);
9647 exprs.push(retValue);
9648 var node = make_sequence(self, exprs).optimize(compressor);
9649 fn.inlined = save_inlined;
9650 node = maintain_this_binding(compressor, parent, current, node);
9651 if (replacing || best_of_expression(node, self) === node) {
9652 refs.forEach(function(ref) {
9653 ref.scope = exp === fn ? fn.parent_scope : exp.scope;
9655 var def = ref.definition();
9656 if (replacing) def.replaced++;
9657 def.single_use = false;
9660 } else if (!node.has_side_effects(compressor)) {
9661 self.drop_side_effect_free = function(compressor, first_in_statement) {
9663 var exprs = self.args.slice();
9664 exprs.unshift(self.expression);
9665 return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement);
9669 var arg_used, insert, in_loop, scope;
9670 if (replacing && can_inject_symbols()) {
9671 fn._squeezed = true;
9672 if (exp !== fn) fn.parent_scope = exp.scope;
9673 var node = make_sequence(self, flatten_fn()).optimize(compressor);
9674 return maintain_this_binding(compressor, parent, current, node);
9677 if (compressor.option("side_effects")
9679 && all(fn.body, is_empty)
9680 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
9681 && !(is_arrow(fn) && fn.value)
9682 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9683 return make_sequence(self, convert_args()).optimize(compressor);
9686 if (compressor.option("drop_console")) {
9687 if (exp instanceof AST_PropAccess) {
9688 var name = exp.expression;
9689 while (name.expression) {
9690 name = name.expression;
9692 if (is_undeclared_ref(name) && name.name == "console") {
9693 return make_node(AST_Undefined, self).optimize(compressor);
9697 if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
9698 return self.negate(compressor, true);
9700 return try_evaluate(compressor, self);
9702 function has_arg_refs(node) {
9704 node.walk(new TreeWalker(function(node) {
9705 if (found) return true;
9706 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
9707 return found = true;
9713 function make_void_lhs(orig) {
9714 return make_node(AST_Dot, orig, {
9715 expression: make_node(AST_Array, orig, { elements: [] }),
9720 function convert_args(value) {
9721 var args = self.args.slice();
9722 var destructured = has_default > 1 || has_destructured || fn.rest;
9723 if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
9725 var tt = new TreeTransformer(function(node, descend) {
9726 if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
9727 name: node.name.transform(tt) || make_void_lhs(node),
9730 if (node instanceof AST_DestructuredArray) {
9732 node.elements.forEach(function(node, index) {
9733 node = node.transform(tt);
9734 if (node) elements[index] = node;
9736 fill_holes(node, elements);
9737 return make_node(AST_DestructuredArray, node, { elements: elements });
9739 if (node instanceof AST_DestructuredObject) {
9740 var properties = [], side_effects = [];
9741 node.properties.forEach(function(prop) {
9743 var value = prop.value.transform(tt);
9745 if (side_effects.length) {
9746 if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop);
9747 side_effects.push(key);
9748 key = make_sequence(node, side_effects);
9751 properties.push(make_node(AST_DestructuredKeyVal, prop, {
9755 } else if (key instanceof AST_Node) {
9756 side_effects.push(key);
9759 if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
9760 key: make_sequence(node, side_effects),
9761 value: make_void_lhs(node),
9763 return make_node(AST_DestructuredObject, node, { properties: properties });
9765 if (node instanceof AST_SymbolFunarg) return null;
9768 fn.argnames.forEach(function(argname, index) {
9769 argname = argname.transform(tt);
9770 if (argname) lhs[index] = argname;
9772 var rest = fn.rest && fn.rest.transform(tt);
9773 if (rest) lhs.length = fn.argnames.length;
9774 fill_holes(fn, lhs);
9775 args[0] = make_node(AST_Assign, self, {
9777 left: make_node(AST_DestructuredArray, fn, {
9783 } else fn.argnames.forEach(function(argname) {
9784 if (argname instanceof AST_DefaultValue) args.push(argname.value);
9786 args.push(value || make_node(AST_Undefined, self));
9790 function safe_from_await_yield(node, scope) {
9791 var avoid = avoid_await_yield(scope);
9792 if (!avoid) return true;
9794 var tw = new TreeWalker(function(node) {
9795 if (!safe) return true;
9796 if (node instanceof AST_Scope) {
9797 if (node === fn) return;
9798 if (is_arrow(node)) {
9799 for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
9800 } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
9805 if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
9811 function noop_value() {
9812 return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
9815 function return_value(stat) {
9816 if (!stat) return noop_value();
9817 if (stat instanceof AST_Return) return stat.value || noop_value();
9818 if (stat instanceof AST_SimpleStatement) {
9819 return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, {
9821 expression: stat.body,
9826 function can_flatten_body(stat) {
9827 var len = fn.body.length;
9829 stat = return_value(stat);
9830 if (stat) return stat;
9832 if (compressor.option("inline") < 3) return false;
9834 for (var i = 0; i < len; i++) {
9835 var line = fn.body[i];
9836 if (line instanceof AST_Var) {
9837 var assigned = var_assigned || !declarations_only(line);
9839 var_assigned = true;
9840 if (stat) return false;
9842 } else if (line instanceof AST_AsyncDefun
9843 || line instanceof AST_Defun
9844 || line instanceof AST_EmptyStatement) {
9852 return return_value(stat);
9855 function resolve_index(def) {
9856 for (var i = fn.argnames.length; --i >= 0;) {
9857 if (fn.argnames[i].definition() === def) return i;
9861 function can_substitute_directly() {
9862 if (has_default || has_destructured || var_assigned || fn.rest) return;
9863 if (compressor.option("inline") < 2 && fn.argnames.length) return;
9864 if (!fn.variables.all(function(def) {
9865 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
9867 var scope = compressor.find_parent(AST_Scope);
9869 var avoid = avoid_await_yield(scope);
9872 var side_effects = false;
9873 value.walk(new TreeWalker(function(node, descend) {
9874 if (abort) return true;
9875 if (node instanceof AST_Binary && lazy_op[node.operator]
9876 || node instanceof AST_Conditional) {
9880 if (node instanceof AST_Scope) return abort = true;
9881 if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
9882 if (node instanceof AST_SymbolRef) {
9883 var def = node.definition();
9884 if (fn.variables.get(node.name) !== def) {
9888 if (def.init instanceof AST_LambdaDefinition) return abort = true;
9889 if (is_lhs(node, this.parent())) return abort = true;
9890 var index = resolve_index(def);
9891 if (!(begin < index)) begin = index;
9892 if (!in_order) return;
9896 in_order.push(fn.argnames[index]);
9900 if (node.has_side_effects(compressor)) {
9902 side_effects = true;
9907 var end = self.args.length;
9908 if (in_order && fn.argnames.length >= end) {
9909 end = fn.argnames.length;
9910 while (end-- > begin && fn.argnames[end] === in_order.pop());
9913 return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) {
9914 return funarg.is_constant_expression(scope);
9915 } : function(funarg) {
9916 return !funarg.has_side_effects(compressor);
9920 function var_exists(defined, name) {
9921 return defined[name] || identifier_atom[name] || scope.var_names()[name];
9924 function can_inject_args(defined, used, safe_to_inject) {
9926 fn.each_argname(function(arg) {
9928 if (arg.__unused) return;
9929 if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
9930 used[arg.name] = true;
9931 if (in_loop) in_loop.push(arg.definition());
9936 function can_inject_vars(defined, used, safe_to_inject) {
9937 for (var i = 0; i < fn.body.length; i++) {
9938 var stat = fn.body[i];
9939 if (stat instanceof AST_LambdaDefinition) {
9940 if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
9941 if (!all(stat.enclosed, function(def) {
9942 return def.scope === stat || !defined[def.name];
9944 if (in_loop) in_loop.push(stat.name.definition());
9947 if (!(stat instanceof AST_Var)) continue;
9948 if (!safe_to_inject) return false;
9949 for (var j = stat.definitions.length; --j >= 0;) {
9950 var name = stat.definitions[j].name;
9951 if (var_exists(defined, name.name)) return false;
9952 if (in_loop) in_loop.push(name.definition());
9958 function can_inject_symbols() {
9959 var defined = Object.create(null);
9960 var level = 0, child;
9963 if (scope.variables) scope.variables.each(function(def) {
9964 defined[def.name] = true;
9967 scope = compressor.parent(level++);
9968 if (scope instanceof AST_DWLoop) {
9970 } else if (scope instanceof AST_For) {
9971 if (scope.init === child) continue;
9973 } else if (scope instanceof AST_ForEnumeration) {
9974 if (scope.init === child) continue;
9975 if (scope.object === child) continue;
9977 } else if (scope instanceof AST_SymbolRef) {
9978 if (scope.fixed_value() instanceof AST_Scope) return false;
9980 } while (!(scope instanceof AST_Scope));
9981 insert = scope.body.indexOf(child) + 1;
9982 if (!insert) return false;
9983 if (!safe_from_await_yield(fn, scope)) return false;
9984 var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
9985 if (scope instanceof AST_Toplevel) {
9986 if (compressor.toplevel.vars) {
9987 defined["arguments"] = true;
9989 safe_to_inject = false;
9992 var inline = compressor.option("inline");
9993 arg_used = Object.create(defined);
9994 if (!can_inject_args(defined, arg_used, inline >= 2 && safe_to_inject)) return false;
9995 var used = Object.create(arg_used);
9996 if (!can_inject_vars(defined, used, inline >= 3 && safe_to_inject)) return false;
9997 return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
10000 function append_var(decls, expressions, name, value) {
10001 var def = name.definition();
10002 if (!scope.var_names()[name.name]) {
10003 scope.var_names()[name.name] = true;
10004 decls.push(make_node(AST_VarDef, name, {
10009 scope.variables.set(name.name, def);
10010 scope.enclosed.push(def);
10011 if (!value) return;
10012 var sym = make_node(AST_SymbolRef, name, name);
10013 def.references.push(sym);
10014 expressions.push(make_node(AST_Assign, self, {
10021 function flatten_args(decls, expressions) {
10022 var len = fn.argnames.length;
10023 for (var i = self.args.length; --i >= len;) {
10024 expressions.push(self.args[i]);
10026 var default_args = [];
10027 for (i = len; --i >= 0;) {
10028 var argname = fn.argnames[i];
10030 if (argname instanceof AST_DefaultValue) {
10031 default_args.push(argname);
10032 name = argname.name;
10036 var value = self.args[i];
10037 if (name.__unused || scope.var_names()[name.name]) {
10038 if (value) expressions.push(value);
10040 var symbol = make_node(AST_SymbolVar, name, name);
10041 name.definition().orig.push(symbol);
10042 if ("__unused" in name) {
10043 append_var(decls, expressions, symbol);
10044 if (value) expressions.push(value);
10046 if (!value && in_loop && argname === name) value = make_node(AST_Undefined, self);
10047 append_var(decls, expressions, symbol, value);
10052 expressions.reverse();
10053 for (i = default_args.length; --i >= 0;) {
10054 var node = default_args[i];
10055 if ("__unused" in node.name) {
10056 expressions.push(node.value);
10058 var sym = make_node(AST_SymbolRef, node.name, node.name);
10059 node.name.definition().references.push(sym);
10060 expressions.push(make_node(AST_Assign, node, {
10069 function flatten_destructured(decls, expressions) {
10070 expressions.push(make_node(AST_Assign, self, {
10072 left: make_node(AST_DestructuredArray, self, {
10073 elements: fn.argnames.map(function(argname) {
10074 if (argname.__unused) return make_node(AST_Hole, argname);
10075 return argname.convert_symbol(AST_SymbolRef, process);
10077 rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
10079 right: make_node(AST_Array, self, { elements: self.args.slice() }),
10082 function process(ref, name) {
10083 var def = name.definition();
10084 def.references.push(ref);
10085 var symbol = make_node(AST_SymbolVar, name, name);
10086 def.orig.push(symbol);
10087 append_var(decls, expressions, symbol);
10091 function flatten_var(name) {
10092 var redef = name.definition().redefined();
10094 name = name.clone();
10095 name.thedef = redef;
10100 function flatten_vars(decls, expressions) {
10101 var args = [ insert, 0 ];
10102 var decl_var = [], expr_var = [], expr_loop = [];
10103 for (var i = 0; i < fn.body.length; i++) {
10104 var stat = fn.body[i];
10105 if (stat instanceof AST_LambdaDefinition) {
10107 var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
10108 name.definition().orig.push(name);
10109 append_var(decls, expressions, name, to_func_expr(stat, true));
10111 var def = stat.name.definition();
10112 scope.functions.set(def.name, def);
10113 scope.variables.set(def.name, def);
10114 scope.enclosed.push(def);
10115 scope.var_names()[def.name] = true;
10120 if (!(stat instanceof AST_Var)) continue;
10121 for (var j = 0; j < stat.definitions.length; j++) {
10122 var var_def = stat.definitions[j];
10123 var name = flatten_var(var_def.name);
10124 append_var(decl_var, expr_var, name, var_def.value);
10125 if (in_loop && !HOP(arg_used, name.name)) {
10126 var def = fn.variables.get(name.name);
10127 var sym = make_node(AST_SymbolRef, name, name);
10128 def.references.push(sym);
10129 expr_loop.push(make_node(AST_Assign, var_def, {
10132 right: make_node(AST_Undefined, name),
10137 [].push.apply(decls, decl_var);
10138 [].push.apply(expressions, expr_loop);
10139 [].push.apply(expressions, expr_var);
10143 function flatten_fn() {
10145 var expressions = [];
10146 if (has_default > 1 || has_destructured || fn.rest) {
10147 flatten_destructured(decls, expressions);
10149 flatten_args(decls, expressions);
10151 var args = flatten_vars(decls, expressions);
10152 expressions.push(value);
10153 if (decls.length) args.push(make_node(AST_Var, fn, {
10156 [].splice.apply(scope.body, args);
10157 fn.enclosed.forEach(function(def) {
10158 if (scope.var_names()[def.name]) return;
10159 scope.enclosed.push(def);
10160 scope.var_names()[def.name] = true;
10162 return expressions;
10166 OPT(AST_New, function(self, compressor) {
10167 if (compressor.option("sequences")) {
10168 var seq = lift_sequence_in_expression(self, compressor);
10169 if (seq !== self) return seq.optimize(compressor);
10171 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
10172 if (compressor.option("unsafe")) {
10173 var exp = self.expression;
10174 if (is_undeclared_ref(exp)) {
10175 switch (exp.name) {
10181 return make_node(AST_Call, self, self).transform(compressor);
10188 // (a = b, x && a = c) ---> a = x ? c : b
10189 // (a = b, x || a = c) ---> a = x ? b : c
10190 function to_conditional_assignment(compressor, def, value, node) {
10191 if (!(node instanceof AST_Binary)) return;
10192 if (!(node.operator == "&&" || node.operator == "||")) return;
10193 if (!(node.right instanceof AST_Assign)) return;
10194 if (node.right.operator != "=") return;
10195 if (!(node.right.left instanceof AST_SymbolRef)) return;
10196 if (node.right.left.definition() !== def) return;
10197 if (value.has_side_effects(compressor)) return;
10198 if (!safe_from_assignment(node.left)) return;
10199 if (!safe_from_assignment(node.right.right)) return;
10201 return node.operator == "&&" ? make_node(AST_Conditional, node, {
10202 condition: node.left,
10203 consequent: node.right.right,
10205 }) : make_node(AST_Conditional, node, {
10206 condition: node.left,
10208 alternative: node.right.right
10211 function safe_from_assignment(node) {
10212 if (node.has_side_effects(compressor)) return;
10214 node.walk(new TreeWalker(function(node) {
10215 if (hit) return true;
10216 if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
10222 OPT(AST_Sequence, function(self, compressor) {
10223 var expressions = filter_for_side_effects();
10224 var end = expressions.length - 1;
10225 merge_assignments();
10226 trim_right_for_undefined();
10228 self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
10229 if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
10232 self.expressions = expressions;
10235 function filter_for_side_effects() {
10236 if (!compressor.option("side_effects")) return self.expressions;
10237 var expressions = [];
10238 var first = first_in_statement(compressor);
10239 var last = self.expressions.length - 1;
10240 self.expressions.forEach(function(expr, index) {
10241 if (index < last) expr = expr.drop_side_effect_free(compressor, first);
10243 merge_sequence(expressions, expr);
10247 return expressions;
10250 function trim_right_for_undefined() {
10251 if (!compressor.option("side_effects")) return;
10252 while (end > 0 && is_undefined(expressions[end], compressor)) end--;
10253 if (end < expressions.length - 1) {
10254 expressions[end] = make_node(AST_UnaryPrefix, self, {
10256 expression : expressions[end]
10258 expressions.length = end + 1;
10262 function is_simple_assign(node) {
10263 return node instanceof AST_Assign
10264 && node.operator == "="
10265 && node.left instanceof AST_SymbolRef
10266 && node.left.definition();
10269 function merge_assignments() {
10270 for (var i = 1; i < end; i++) {
10271 var prev = expressions[i - 1];
10272 var def = is_simple_assign(prev);
10273 if (!def) continue;
10274 var expr = expressions[i];
10275 if (compressor.option("conditionals")) {
10276 var cond = to_conditional_assignment(compressor, def, prev.right, expr);
10279 expressions.splice(i--, 1);
10284 if (compressor.option("dead_code")
10285 && is_simple_assign(expr) === def
10286 && expr.right.is_constant_expression(def.scope.resolve())) {
10287 expressions[--i] = prev.right;
10293 OPT(AST_UnaryPostfix, function(self, compressor) {
10294 if (compressor.option("sequences")) {
10295 var seq = lift_sequence_in_expression(self, compressor);
10296 if (seq !== self) return seq.optimize(compressor);
10298 return try_evaluate(compressor, self);
10301 var SIGN_OPS = makePredicate("+ -");
10302 var MULTIPLICATIVE_OPS = makePredicate("* / %");
10303 OPT(AST_UnaryPrefix, function(self, compressor) {
10304 var op = self.operator;
10305 var exp = self.expression;
10306 if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
10307 return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
10309 if (compressor.option("sequences") && can_lift()) {
10310 var seq = lift_sequence_in_expression(self, compressor);
10311 if (seq !== self) return seq.optimize(compressor);
10313 if (compressor.option("side_effects") && op == "void") {
10314 exp = exp.drop_side_effect_free(compressor);
10315 if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
10316 self.expression = exp;
10319 if (compressor.option("booleans")) {
10320 if (op == "!" && exp.is_truthy()) {
10321 return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
10322 } else if (compressor.in_boolean_context()) switch (op) {
10324 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
10325 // !!foo ---> foo, if we're in boolean context
10326 return exp.expression;
10328 if (exp instanceof AST_Binary) {
10329 self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
10333 // typeof always returns a non-empty string, thus it's
10334 // always true in booleans
10335 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
10336 var exprs = [ make_node(AST_True, self) ];
10337 if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
10338 return make_sequence(self, exprs).optimize(compressor);
10341 if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
10342 if (compressor.option("evaluate")
10343 && exp instanceof AST_Binary
10345 && MULTIPLICATIVE_OPS[exp.operator]
10346 && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
10347 return make_node(AST_Binary, self, {
10348 operator: exp.operator,
10349 left: make_node(AST_UnaryPrefix, exp.left, {
10351 expression: exp.left
10356 // avoids infinite recursion of numerals
10357 return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
10358 ? self : try_evaluate(compressor, self);
10360 function may_not_delete(node) {
10361 return node instanceof AST_Infinity
10362 || node instanceof AST_NaN
10363 || node instanceof AST_NewTarget
10364 || node instanceof AST_PropAccess
10365 || node instanceof AST_SymbolRef
10366 || node instanceof AST_Undefined;
10369 function can_lift() {
10372 return !may_not_delete(exp.tail_node());
10374 return !is_undeclared_ref(exp.tail_node());
10381 OPT(AST_Await, function(self, compressor) {
10382 if (!compressor.option("awaits")) return self;
10383 if (compressor.option("sequences")) {
10384 var seq = lift_sequence_in_expression(self, compressor);
10385 if (seq !== self) return seq.optimize(compressor);
10387 if (compressor.option("side_effects")) {
10388 var exp = self.expression;
10389 if (exp instanceof AST_Await) return exp.optimize(compressor);
10390 if (exp instanceof AST_UnaryPrefix) {
10391 if (exp.expression instanceof AST_Await) return exp.optimize(compressor);
10392 if (exp.operator == "void") return make_node(AST_UnaryPrefix, self, {
10394 expression: make_node(AST_Await, self, { expression: exp.expression }),
10395 }).optimize(compressor);
10397 for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) {
10398 if (is_arrow(parent)) {
10399 if (parent.value === node) return exp.optimize(compressor);
10400 } else if (parent instanceof AST_Return) {
10404 parent = compressor.parent(level++);
10405 if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) {
10409 } while (parent && !(parent instanceof AST_Scope));
10410 if (drop) return exp.optimize(compressor);
10411 } else if (parent instanceof AST_Sequence) {
10412 if (parent.tail_node() === node) continue;
10420 OPT(AST_Yield, function(self, compressor) {
10421 if (!compressor.option("yields")) return self;
10422 if (compressor.option("sequences")) {
10423 var seq = lift_sequence_in_expression(self, compressor);
10424 if (seq !== self) return seq.optimize(compressor);
10426 var exp = self.expression;
10427 if (self.nested && exp.TYPE == "Call") {
10428 var inlined = exp.clone().optimize(compressor);
10429 if (inlined.TYPE != "Call") return inlined;
10434 AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
10435 if (this.left instanceof AST_PropAccess) {
10436 if (!(this.left.expression instanceof AST_Sequence)) return this;
10437 var x = this.left.expression.expressions.slice();
10438 var e = this.clone();
10439 e.left = e.left.clone();
10440 e.left.expression = x.pop();
10442 return make_sequence(this, x);
10444 if (this.left instanceof AST_Sequence) {
10445 var x = this.left.expressions.slice();
10446 var e = this.clone();
10449 return make_sequence(this, x);
10451 if (this.right instanceof AST_Sequence) {
10452 if (this.left.has_side_effects(compressor)) return this;
10453 var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
10454 var x = this.right.expressions;
10455 var last = x.length - 1;
10456 for (var i = 0; i < last; i++) {
10457 if (!assign && x[i].has_side_effects(compressor)) break;
10461 var e = this.clone();
10464 return make_sequence(this, x);
10467 var e = this.clone();
10468 e.right = make_sequence(this.right, x.slice(i));
10471 return make_sequence(this, x);
10477 var indexFns = makePredicate("indexOf lastIndexOf");
10478 var commutativeOperators = makePredicate("== === != !== * & | ^");
10479 function is_object(node) {
10480 if (node instanceof AST_Assign) return node.operator == "=" && is_object(node.right);
10481 if (node instanceof AST_Sequence) return is_object(node.tail_node());
10482 if (node instanceof AST_SymbolRef) return is_object(node.fixed_value());
10483 return node instanceof AST_Array
10484 || node instanceof AST_Class
10485 || node instanceof AST_Lambda
10486 || node instanceof AST_New
10487 || node instanceof AST_Object;
10490 function is_primitive(compressor, node) {
10491 if (node.is_constant()) return true;
10492 if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right);
10493 if (node instanceof AST_Binary) {
10494 return !lazy_op[node.operator]
10495 || is_primitive(compressor, node.left) && is_primitive(compressor, node.right);
10497 if (node instanceof AST_Conditional) {
10498 return is_primitive(compressor, node.consequent) && is_primitive(compressor, node.alternative);
10500 if (node instanceof AST_Sequence) return is_primitive(compressor, node.tail_node());
10501 if (node instanceof AST_SymbolRef) {
10502 var fixed = node.fixed_value();
10503 return fixed && is_primitive(compressor, fixed);
10505 if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag);
10506 if (node instanceof AST_Unary) return true;
10509 function repeatable(compressor, node) {
10510 if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
10511 if (node instanceof AST_Sub) {
10512 return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
10514 if (node instanceof AST_Symbol) return true;
10515 return !node.has_side_effects(compressor);
10518 OPT(AST_Binary, function(self, compressor) {
10519 function reversible() {
10520 return self.left.is_constant()
10521 || self.right.is_constant()
10522 || !self.left.has_side_effects(compressor)
10523 && !self.right.has_side_effects(compressor);
10525 function reverse(op) {
10526 if (reversible()) {
10527 if (op) self.operator = op;
10528 var tmp = self.left;
10529 self.left = self.right;
10533 function swap_chain() {
10534 var rhs = self.right;
10535 self.left = make_node(AST_Binary, self, {
10536 operator: self.operator,
10539 start: self.left.start,
10542 self.right = rhs.right;
10543 self.left = self.left.transform(compressor);
10545 if (commutativeOperators[self.operator]
10546 && self.right.is_constant()
10547 && !self.left.is_constant()
10548 && !(self.left instanceof AST_Binary
10549 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10550 // if right is a constant, whatever side effects the
10551 // left side might have could not influence the
10552 // result. hence, force switch.
10555 if (compressor.option("sequences")) {
10556 var seq = self.lift_sequences(compressor);
10557 if (seq !== self) return seq.optimize(compressor);
10559 if (compressor.option("assignments") && lazy_op[self.operator]) {
10560 var assign = self.right;
10561 // a || (a = x) ---> a = a || x
10562 // a && (a = x) ---> a = a && x
10563 if (self.left instanceof AST_SymbolRef
10564 && assign instanceof AST_Assign
10565 && assign.operator == "="
10566 && self.left.equivalent_to(assign.left)) {
10567 self.right = assign.right;
10568 assign.right = self;
10572 if (compressor.option("comparisons")) switch (self.operator) {
10575 if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
10576 AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
10577 return make_sequence(self, [
10579 make_node(self.operator == "===" ? AST_False : AST_True, self)
10580 ]).optimize(compressor);
10582 var is_strict_comparison = true;
10583 if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
10584 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
10585 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
10586 repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
10587 self.operator = self.operator.slice(0, 2);
10589 // XXX: intentionally falling down to the next case
10592 // void 0 == x ---> null == x
10593 if (!is_strict_comparison && is_undefined(self.left, compressor)) {
10594 self.left = make_node(AST_Null, self.left);
10596 // "undefined" == typeof x ---> undefined === x
10597 else if (compressor.option("typeofs")
10598 && self.left instanceof AST_String
10599 && self.left.value == "undefined"
10600 && self.right instanceof AST_UnaryPrefix
10601 && self.right.operator == "typeof") {
10602 var expr = self.right.expression;
10603 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
10604 : !(expr instanceof AST_PropAccess && compressor.option("ie"))) {
10606 self.left = make_node(AST_Undefined, self.left).optimize(compressor);
10607 if (self.operator.length == 2) self.operator += "=";
10610 // obj !== obj ---> false
10611 else if (self.left instanceof AST_SymbolRef
10612 && self.right instanceof AST_SymbolRef
10613 && self.left.definition() === self.right.definition()
10614 && is_object(self.left)) {
10615 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
10620 // void 0 !== x && null !== x ---> null != x
10621 // void 0 === x || null === x ---> null == x
10622 var lhs = self.left;
10623 if (lhs.operator == self.operator) {
10626 if (lhs instanceof AST_Binary
10627 && lhs.operator == (self.operator == "&&" ? "!==" : "===")
10628 && self.right instanceof AST_Binary
10629 && lhs.operator == self.right.operator
10630 && (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
10631 || lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
10632 && !lhs.right.has_side_effects(compressor)
10633 && lhs.right.equivalent_to(self.right.right)) {
10634 var combined = make_node(AST_Binary, self, {
10635 operator: lhs.operator.slice(0, -1),
10636 left: make_node(AST_Null, self),
10639 if (lhs !== self.left) {
10640 combined = make_node(AST_Binary, self, {
10641 operator: self.operator,
10642 left: self.left.left,
10650 var in_bool = false;
10651 var parent = compressor.parent();
10652 if (compressor.option("booleans")) {
10653 var lhs = self.left;
10654 if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
10655 if (lhs.equivalent_to(self.right)) {
10656 return maintain_this_binding(compressor, parent, compressor.self(), lhs).optimize(compressor);
10658 mark_duplicate_condition(compressor, lhs);
10660 in_bool = compressor.in_boolean_context();
10662 if (in_bool) switch (self.operator) {
10664 var ll = self.left.evaluate(compressor);
10665 var rr = self.right.evaluate(compressor);
10666 if (ll && typeof ll == "string") {
10667 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10668 return make_sequence(self, [
10670 make_node(AST_True, self)
10671 ]).optimize(compressor);
10673 if (rr && typeof rr == "string") {
10674 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10675 return make_sequence(self, [
10677 make_node(AST_True, self)
10678 ]).optimize(compressor);
10682 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10683 return make_node(AST_UnaryPrefix, self, {
10685 expression: self.right
10686 }).optimize(compressor);
10690 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10691 return self.right.optimize(compressor);
10695 if (compressor.option("comparisons") && self.is_boolean(compressor)) {
10696 if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
10697 var negated = best_of(compressor, self, make_node(AST_UnaryPrefix, self, {
10699 expression: self.negate(compressor, first_in_statement(compressor))
10701 if (negated !== self) return negated;
10703 switch (self.operator) {
10704 case ">": reverse("<"); break;
10705 case ">=": reverse("<="); break;
10708 // x && (y && z) ---> x && y && z
10709 // x || (y || z) ---> x || y || z
10710 if (compressor.option("conditionals")
10711 && lazy_op[self.operator]
10712 && self.right instanceof AST_Binary
10713 && self.operator == self.right.operator) {
10716 if (compressor.option("strings") && self.operator == "+") {
10717 // "foo" + 42 + "" ---> "foo" + 42
10718 if (self.right instanceof AST_String
10719 && self.right.value == ""
10720 && self.left.is_string(compressor)) {
10721 return self.left.optimize(compressor);
10723 // "" + ("foo" + 42) ---> "foo" + 42
10724 if (self.left instanceof AST_String
10725 && self.left.value == ""
10726 && self.right.is_string(compressor)) {
10727 return self.right.optimize(compressor);
10729 // "" + 42 + "foo" ---> 42 + "foo"
10730 if (self.left instanceof AST_Binary
10731 && self.left.operator == "+"
10732 && self.left.left instanceof AST_String
10733 && self.left.left.value == ""
10734 && self.right.is_string(compressor)
10735 && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) {
10736 self.left = self.left.right;
10737 return self.optimize(compressor);
10739 // "x" + (y + "z") ---> "x" + y + "z"
10740 // x + ("y" + z) ---> x + "y" + z
10741 if (self.right instanceof AST_Binary
10742 && self.operator == self.right.operator
10743 && (self.left.is_string(compressor) && self.right.is_string(compressor)
10744 || self.right.left.is_string(compressor)
10745 && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
10749 if (compressor.option("evaluate")) {
10750 var associative = true;
10751 switch (self.operator) {
10753 var ll = fuzzy_eval(compressor, self.left);
10755 AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
10756 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10757 } else if (!(ll instanceof AST_Node)) {
10758 AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
10759 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10761 var rr = self.right.evaluate(compressor);
10764 AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
10765 return make_sequence(self, [
10767 make_node(AST_False, self)
10768 ]).optimize(compressor);
10769 } else self.falsy = true;
10770 } else if (!(rr instanceof AST_Node)) {
10771 if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
10772 AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
10773 return self.left.optimize(compressor);
10776 // (x || false) && y ---> x ? y : false
10777 if (self.left.operator == "||") {
10778 var lr = fuzzy_eval(compressor, self.left.right);
10779 if (!lr) return make_node(AST_Conditional, self, {
10780 condition: self.left.left,
10781 consequent: self.right,
10782 alternative: self.left.right
10783 }).optimize(compressor);
10787 var nullish = true;
10789 var ll = fuzzy_eval(compressor, self.left, nullish);
10790 if (nullish ? ll == null : !ll) {
10791 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10792 operator: self.operator,
10793 value: nullish ? "nulish" : "false",
10794 file: self.start.file,
10795 line: self.start.line,
10796 col: self.start.col,
10798 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10799 } else if (!(ll instanceof AST_Node)) {
10800 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10801 operator: self.operator,
10802 value: nullish ? "defined" : "true",
10803 file: self.start.file,
10804 line: self.start.line,
10805 col: self.start.col,
10807 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10809 var rr = self.right.evaluate(compressor);
10811 if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
10812 AST_Node.warn("Dropping side-effect-free {operator} [{file}:{line},{col}]", {
10813 operator: self.operator,
10814 file: self.start.file,
10815 line: self.start.line,
10816 col: self.start.col,
10818 return self.left.optimize(compressor);
10820 } else if (!nullish && !(rr instanceof AST_Node)) {
10822 AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
10823 return make_sequence(self, [
10825 make_node(AST_True, self)
10826 ]).optimize(compressor);
10827 } else self.truthy = true;
10829 // x && true || y ---> x ? true : y
10830 if (!nullish && self.left.operator == "&&") {
10831 var lr = fuzzy_eval(compressor, self.left.right);
10832 if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
10833 condition: self.left.left,
10834 consequent: self.left.right,
10835 alternative: self.right
10836 }).optimize(compressor);
10840 // "foo" + ("bar" + x) ---> "foobar" + x
10841 if (self.left instanceof AST_Constant
10842 && self.right instanceof AST_Binary
10843 && self.right.operator == "+"
10844 && self.right.left instanceof AST_Constant
10845 && self.right.is_string(compressor)) {
10846 self = make_node(AST_Binary, self, {
10848 left: make_node(AST_String, self.left, {
10849 value: "" + self.left.value + self.right.left.value,
10850 start: self.left.start,
10851 end: self.right.left.end
10853 right: self.right.right
10856 // (x + "foo") + "bar" ---> x + "foobar"
10857 if (self.right instanceof AST_Constant
10858 && self.left instanceof AST_Binary
10859 && self.left.operator == "+"
10860 && self.left.right instanceof AST_Constant
10861 && self.left.is_string(compressor)) {
10862 self = make_node(AST_Binary, self, {
10864 left: self.left.left,
10865 right: make_node(AST_String, self.right, {
10866 value: "" + self.left.right.value + self.right.value,
10867 start: self.left.right.start,
10868 end: self.right.end
10872 // a + -b ---> a - b
10873 if (self.right instanceof AST_UnaryPrefix
10874 && self.right.operator == "-"
10875 && self.left.is_number(compressor)) {
10876 self = make_node(AST_Binary, self, {
10879 right: self.right.expression
10883 // -a + b ---> b - a
10884 if (self.left instanceof AST_UnaryPrefix
10885 && self.left.operator == "-"
10887 && self.right.is_number(compressor)) {
10888 self = make_node(AST_Binary, self, {
10891 right: self.left.expression
10895 // (a + b) + 3 ---> 3 + (a + b)
10896 if (compressor.option("unsafe_math")
10897 && self.left instanceof AST_Binary
10898 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10899 && self.right.is_constant()
10900 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10901 && self.left.is_number(compressor)
10902 && !self.left.right.is_constant()
10903 && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
10904 self = make_node(AST_Binary, self, {
10905 operator: self.left.operator,
10906 left: make_node(AST_Binary, self, {
10907 operator: self.operator,
10909 right: self.left.left
10911 right: self.left.right
10916 // a - -b ---> a + b
10917 if (self.right instanceof AST_UnaryPrefix
10918 && self.right.operator == "-"
10919 && self.left.is_number(compressor)
10920 && self.right.expression.is_number(compressor)) {
10921 self = make_node(AST_Binary, self, {
10924 right: self.right.expression
10930 associative = compressor.option("unsafe_math");
10931 // +a - b ---> a - b
10932 // a - +b ---> a - b
10933 if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
10934 var node = self[operand];
10935 if (node instanceof AST_UnaryPrefix && node.operator == "+") {
10936 var exp = node.expression;
10937 if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) {
10938 self[operand] = exp;
10945 // a + +b ---> +b + a
10946 if (self.operator != "-"
10947 && self.operator != "/"
10948 && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
10949 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10951 && !(self.left instanceof AST_Binary
10952 && self.left.operator != self.operator
10953 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10954 var reversed = make_node(AST_Binary, self, {
10955 operator: self.operator,
10959 if (self.right instanceof AST_Constant
10960 && !(self.left instanceof AST_Constant)) {
10961 self = best_of(compressor, reversed, self);
10963 self = best_of(compressor, self, reversed);
10966 if (!associative || !self.is_number(compressor)) break;
10967 // a + (b + c) ---> (a + b) + c
10968 if (self.right instanceof AST_Binary
10969 && self.right.operator != "%"
10970 && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
10971 && self.right.is_number(compressor)
10972 && (self.operator != "+"
10973 || self.right.left.is_boolean(compressor)
10974 || self.right.left.is_number(compressor))
10975 && (self.operator != "-" || !self.left.is_negative_zero())
10976 && (self.right.left.is_constant_expression()
10977 || !self.right.right.has_side_effects(compressor))
10978 && !is_modify_array(self.right.right)) {
10979 self = make_node(AST_Binary, self, {
10980 operator: align(self.operator, self.right.operator),
10981 left: make_node(AST_Binary, self.left, {
10982 operator: self.operator,
10984 right: self.right.left,
10985 start: self.left.start,
10986 end: self.right.left.end
10988 right: self.right.right
10990 if (self.operator == "+"
10991 && !self.right.is_boolean(compressor)
10992 && !self.right.is_number(compressor)) {
10993 self.right = make_node(AST_UnaryPrefix, self.right, {
10995 expression: self.right
10999 // (2 * n) * 3 ---> 6 * n
11000 // (n + 2) + 3 ---> n + 5
11001 if (self.right instanceof AST_Constant
11002 && self.left instanceof AST_Binary
11003 && self.left.operator != "%"
11004 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
11005 && self.left.is_number(compressor)) {
11006 if (self.left.left instanceof AST_Constant) {
11007 var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
11008 self = make_binary(self, self.left.operator, try_evaluate(compressor, lhs), self.left.right);
11009 } else if (self.left.right instanceof AST_Constant) {
11010 var op = align(self.left.operator, self.operator);
11011 var rhs = try_evaluate(compressor, make_binary(self.left, op, self.left.right, self.right));
11012 if (rhs.is_constant()
11013 && !(self.left.operator == "-"
11014 && self.right.value != 0
11016 && self.left.left.is_negative_zero())) {
11017 self = make_binary(self, self.left.operator, self.left.left, rhs);
11023 if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11024 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
11027 if (self.left.value == 0) {
11028 if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11030 expression: self.right
11031 }).optimize(compressor);
11032 if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
11037 if (self.left.value == 1) {
11038 return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
11040 expression: self.right
11041 }).optimize(compressor);
11045 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
11048 if (self.right.value == 0) {
11049 if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11051 expression: self.left
11052 }).optimize(compressor);
11053 if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
11058 if (self.right.value == 0) {
11059 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11061 expression: self.left
11062 }).optimize(compressor);
11067 if (self.right.value == 1) {
11068 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11070 expression: self.left
11071 }).optimize(compressor);
11077 if (compressor.option("typeofs")) switch (self.operator) {
11079 mark_locally_defined(self.left, self.right, null);
11082 mark_locally_defined(self.left, null, self.right);
11085 if (compressor.option("unsafe")) {
11086 var indexRight = is_indexFn(self.right);
11089 && (self.operator == "==" || self.operator == "!=")
11090 && self.left instanceof AST_Number
11091 && self.left.value == 0) {
11092 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
11094 expression: self.right
11095 }) : self.right).optimize(compressor);
11097 var indexLeft = is_indexFn(self.left);
11098 if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
11099 var node = make_node(AST_UnaryPrefix, self, {
11101 expression: make_node(AST_UnaryPrefix, self, {
11103 expression: indexLeft ? self.left : self.right
11106 switch (self.operator) {
11108 if (indexLeft) break;
11111 node = make_node(AST_UnaryPrefix, self, {
11117 return node.optimize(compressor);
11120 return try_evaluate(compressor, self);
11122 function is_modify_array(node) {
11124 node.walk(new TreeWalker(function(node) {
11125 if (found) return true;
11126 if (node instanceof AST_Assign) {
11127 if (node.left instanceof AST_PropAccess) return found = true;
11128 } else if (node instanceof AST_Unary) {
11129 if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) {
11130 return found = true;
11137 function align(ref, op) {
11140 return op == "+" ? "-" : "+";
11142 return op == "*" ? "/" : "*";
11148 function make_binary(orig, op, left, right, start, end) {
11150 if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
11151 left = make_node(AST_UnaryPrefix, left, {
11156 if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
11157 right = make_node(AST_UnaryPrefix, right, {
11163 return make_node(AST_Binary, orig, {
11172 function is_indexFn(node) {
11173 return node.TYPE == "Call"
11174 && node.expression instanceof AST_Dot
11175 && indexFns[node.expression.property];
11178 function is_indexOf_match_pattern() {
11179 switch (self.operator) {
11181 // 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
11182 return indexRight && self.left instanceof AST_Number && self.left.value == 0;
11184 // array.indexOf(string) < 0 ---> !~array.indexOf(string)
11185 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
11186 // -1 < array.indexOf(string) ---> !!~array.indexOf(string)
11189 // -1 == array.indexOf(string) ---> !~array.indexOf(string)
11190 // -1 != array.indexOf(string) ---> !!~array.indexOf(string)
11191 if (!indexRight) return false;
11192 return self.left instanceof AST_Number && self.left.value == -1
11193 || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
11194 && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
11199 OPT(AST_SymbolExport, function(self) {
11203 function recursive_ref(compressor, def, fn) {
11204 var level = 0, node = compressor.self();
11206 if (node === fn) return node;
11207 if (is_lambda(node) && node.name && node.name.definition() === def) return node;
11208 } while (node = compressor.parent(level++));
11211 function same_scope(def) {
11212 var scope = def.scope.resolve();
11213 return all(def.references, function(ref) {
11214 return scope === ref.scope.resolve();
11218 OPT(AST_SymbolRef, function(self, compressor) {
11219 if (!compressor.option("ie")
11220 && is_undeclared_ref(self)
11221 // testing against `self.scope.uses_with` is an optimization
11222 && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
11223 switch (self.name) {
11225 return make_node(AST_Undefined, self).optimize(compressor);
11227 return make_node(AST_NaN, self).optimize(compressor);
11229 return make_node(AST_Infinity, self).optimize(compressor);
11232 var parent = compressor.parent();
11233 if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
11234 var def = self.definition();
11235 var fixed = self.fixed_value();
11236 var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
11238 if (is_lambda(fixed)) {
11239 if ((def.scope !== self.scope.resolve() || def.in_loop)
11240 && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
11241 single_use = false;
11242 } else if (recursive_ref(compressor, def, fixed)) {
11243 single_use = false;
11244 } else if (fixed.name && fixed.name.definition() !== def) {
11245 single_use = false;
11246 } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
11247 single_use = fixed.is_constant_expression(self.scope);
11248 if (single_use == "f") {
11249 var scope = self.scope;
11251 if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
11252 scope.inlined = true;
11254 } while (scope = scope.parent_scope);
11256 } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
11257 || fixed.name.name == "yield" && is_generator(fixed))) {
11258 single_use = false;
11259 } else if (fixed.has_side_effects(compressor)) {
11260 single_use = false;
11261 } else if (compressor.option("ie") && fixed instanceof AST_Class) {
11262 single_use = false;
11264 if (single_use) fixed.parent_scope = self.scope;
11266 || def.recursive_refs > 0
11267 || !fixed.is_constant_expression()
11268 || fixed.drop_side_effect_free(compressor)) {
11269 single_use = false;
11273 def.single_use = false;
11274 fixed._squeezed = true;
11275 fixed.single_use = true;
11276 if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
11277 if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
11278 if (is_lambda(fixed)) {
11279 var scope = self.scope.resolve();
11280 fixed.enclosed.forEach(function(def) {
11281 if (fixed.variables.has(def.name)) return;
11282 if (scope.var_names()[def.name]) return;
11283 scope.enclosed.push(def);
11284 scope.var_names()[def.name] = true;
11288 if (def.recursive_refs > 0) {
11289 value = fixed.clone(true);
11290 var defun_def = value.name.definition();
11291 var lambda_def = value.variables.get(value.name.name);
11292 var name = lambda_def && lambda_def.orig[0];
11293 var def_fn_name, symbol_type;
11294 if (value instanceof AST_Class) {
11295 def_fn_name = "def_function";
11296 symbol_type = AST_SymbolClass;
11298 def_fn_name = "def_variable";
11299 symbol_type = AST_SymbolLambda;
11301 if (!(name instanceof symbol_type)) {
11302 name = make_node(symbol_type, value.name, value.name);
11303 name.scope = value;
11305 lambda_def = value[def_fn_name](name);
11306 lambda_def.recursive_refs = def.recursive_refs;
11308 value.walk(new TreeWalker(function(node) {
11309 if (node instanceof AST_SymbolDeclaration) {
11310 if (node !== name) {
11311 var def = node.definition();
11312 def.orig.push(node);
11317 if (!(node instanceof AST_SymbolRef)) return;
11318 var def = node.definition();
11319 if (def === defun_def) {
11320 node.thedef = def = lambda_def;
11322 def.single_use = false;
11323 var fn = node.fixed_value();
11326 && fn.name.definition() === def
11327 && def.scope === fn.name.scope
11328 && fixed.variables.get(fn.name.name) === def) {
11329 fn.name = fn.name.clone();
11330 node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
11333 def.references.push(node);
11336 if (fixed instanceof AST_Scope) {
11337 compressor.push(fixed);
11338 value = fixed.optimize(compressor);
11341 value = fixed.optimize(compressor);
11343 value = value.transform(new TreeTransformer(function(node, descend) {
11344 if (node instanceof AST_Scope) return node;
11345 node = node.clone();
11346 descend(node, this);
11353 var local = self.fixed !== def.fixed;
11354 if (fixed && (local || def.should_replace !== false)) {
11356 if (fixed instanceof AST_This) {
11357 if (!is_funarg(def) && same_scope(def)) init = fixed;
11358 } else if ((ev = fixed.evaluate(compressor, true)) !== fixed
11359 && typeof ev != "function"
11361 || typeof ev != "object"
11362 || compressor.option("unsafe_regexp")
11363 && ev instanceof RegExp && !def.cross_loop && same_scope(def))) {
11364 init = make_node_from_constant(ev, fixed);
11367 if (!local && def.should_replace === undefined) {
11368 var value_length = init.optimize(compressor).print_to_string().length;
11369 if (!has_symbol_ref(fixed)) {
11370 value_length = Math.min(value_length, fixed.print_to_string().length);
11372 var name_length = def.name.length;
11373 if (compressor.option("unused") && !compressor.exposed(def)) {
11374 var referenced = def.references.length - def.replaced;
11375 name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
11377 var delta = value_length - Math.floor(name_length);
11378 def.should_replace = delta < compressor.eval_threshold;
11380 if (local || def.should_replace) {
11382 if (has_symbol_ref(fixed)) {
11383 value = init.optimize(compressor);
11384 if (value === init) value = value.clone(true);
11386 value = best_of_expression(init.optimize(compressor), fixed);
11387 if (value === init || value === fixed) value = value.clone(true);
11397 function has_symbol_ref(value) {
11399 value.walk(new TreeWalker(function(node) {
11400 if (node instanceof AST_SymbolRef) found = true;
11401 if (found) return true;
11407 function is_raw_tag(compressor, tag) {
11408 return compressor.option("unsafe")
11409 && tag instanceof AST_Dot
11410 && tag.property == "raw"
11411 && is_undeclared_ref(tag.expression)
11412 && tag.expression.name == "String";
11415 function decode_template(str) {
11416 var malformed = false;
11417 str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
11418 var ch = decode_escape_sequence(seq);
11419 if (typeof ch == "string") return ch;
11422 if (!malformed) return str;
11425 OPT(AST_Template, function(self, compressor) {
11426 if (!compressor.option("templates")) return self;
11427 var tag = self.tag;
11428 if (!tag || is_raw_tag(compressor, tag)) {
11431 for (var i = 0, status; i < self.strings.length; i++) {
11432 var str = self.strings[i];
11434 var trimmed = decode_template(str);
11435 if (trimmed) str = escape_literal(trimmed);
11438 var node = self.expressions[i - 1];
11439 var value = should_join(node);
11441 var prev = strs[strs.length - 1];
11442 var joined = prev + value + str;
11444 if (tag || typeof (decoded = decode_template(joined)) == status) {
11445 strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined;
11452 if (!tag) status = typeof trimmed;
11454 if (!tag && strs.length > 1) {
11455 if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, {
11457 left: make_node(AST_Template, self, {
11458 expressions: exprs.slice(0, -1),
11459 strings: strs.slice(0, -1),
11461 }).transform(compressor),
11462 right: exprs[exprs.length - 1],
11463 }).optimize(compressor);
11464 if (strs[0] == "") {
11465 var left = make_node(AST_Binary, self, {
11467 left: make_node(AST_String, self, { value: "" }),
11470 for (var i = 1; strs[i] == "" && i < exprs.length; i++) {
11471 left = make_node(AST_Binary, self, {
11477 return best_of(compressor, self, make_node(AST_Binary, self, {
11479 left: left.transform(compressor),
11480 right: make_node(AST_Template, self, {
11481 expressions: exprs.slice(i),
11482 strings: strs.slice(i),
11484 }).transform(compressor),
11485 }).optimize(compressor));
11488 self.expressions = exprs;
11489 self.strings = strs;
11491 return try_evaluate(compressor, self);
11493 function escape_literal(str) {
11494 return str.replace(/\r|\\|`|\${/g, function(s) {
11495 return "\\" + (s == "\r" ? "r" : s);
11499 function should_join(node) {
11500 var ev = node.evaluate(compressor);
11501 if (ev === node) return;
11502 if (tag && /\r|\\|`/.test(ev)) return;
11503 ev = escape_literal("" + ev);
11504 if (ev.length > node.print_to_string().length + "${}".length) return;
11509 function is_atomic(lhs, self) {
11510 return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
11513 OPT(AST_Undefined, function(self, compressor) {
11514 if (compressor.option("unsafe_undefined")) {
11515 var undef = find_scope(compressor).find_variable("undefined");
11517 var ref = make_node(AST_SymbolRef, self, {
11518 name : "undefined",
11519 scope : undef.scope,
11522 ref.is_undefined = true;
11526 var lhs = is_lhs(compressor.self(), compressor.parent());
11527 if (lhs && is_atomic(lhs, self)) return self;
11528 return make_node(AST_UnaryPrefix, self, {
11530 expression: make_node(AST_Number, self, {
11536 OPT(AST_Infinity, function(self, compressor) {
11537 var lhs = is_lhs(compressor.self(), compressor.parent());
11538 if (lhs && is_atomic(lhs, self)) return self;
11539 if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) {
11542 return make_node(AST_Binary, self, {
11544 left: make_node(AST_Number, self, {
11547 right: make_node(AST_Number, self, {
11553 OPT(AST_NaN, function(self, compressor) {
11554 var lhs = is_lhs(compressor.self(), compressor.parent());
11555 if (lhs && is_atomic(lhs, self)) return self;
11556 if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
11557 return make_node(AST_Binary, self, {
11559 left: make_node(AST_Number, self, {
11562 right: make_node(AST_Number, self, {
11568 function is_reachable(self, defs) {
11569 var reachable = false;
11570 var find_ref = new TreeWalker(function(node) {
11571 if (reachable) return true;
11572 if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
11573 return reachable = true;
11576 var scan_scope = new TreeWalker(function(node) {
11577 if (reachable) return true;
11578 if (node instanceof AST_Lambda && node !== self) {
11579 if (!(node.name || is_async(node) || is_generator(node))) {
11580 var parent = scan_scope.parent();
11581 if (parent instanceof AST_Call && parent.expression === node) return;
11583 node.walk(find_ref);
11587 self.walk(scan_scope);
11591 var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
11592 var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
11593 OPT(AST_Assign, function(self, compressor) {
11594 if (compressor.option("dead_code")) {
11595 if (self.left instanceof AST_PropAccess) {
11596 if (self.operator == "=") {
11598 var exprs = [ self.left.expression ];
11599 if (self.left instanceof AST_Sub) exprs.push(self.left.property);
11600 exprs.push(self.right);
11601 return make_sequence(self, exprs).optimize(compressor);
11603 if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
11606 var exp = self.left.expression;
11607 if (exp instanceof AST_Lambda
11608 || !compressor.has_directive("use strict")
11609 && exp instanceof AST_Constant
11610 && !exp.may_throw_on_access(compressor)) {
11611 return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
11612 self.left.property,
11614 ]).optimize(compressor);
11617 } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) {
11619 if (self.operator == "=" && self.left.equivalent_to(self.right)
11620 && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11623 if (self.left.is_immutable()) return strip_assignment();
11624 var def = self.left.definition();
11625 var scope = def.scope.resolve();
11626 var local = scope === compressor.find_parent(AST_Lambda);
11627 var level = 0, node;
11628 parent = compressor.self();
11629 if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do {
11631 parent = compressor.parent(level++);
11632 if (parent instanceof AST_Assign) {
11633 if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) {
11634 if (in_try(level, parent)) break;
11635 return strip_assignment(def);
11637 if (parent.left.match_symbol(function(node) {
11638 if (node instanceof AST_PropAccess) return true;
11642 if (parent instanceof AST_Exit) {
11644 if (in_try(level, parent)) break;
11645 if (is_reachable(scope, [ def ])) break;
11646 return strip_assignment(def);
11648 if (parent instanceof AST_SimpleStatement) {
11650 if (is_reachable(scope, [ def ])) break;
11654 parent = compressor.parent(level++);
11655 if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def);
11656 } while (is_tail_block(stat, parent));
11659 if (parent instanceof AST_VarDef) {
11660 if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
11661 if (parent.name.definition() !== def) continue;
11662 if (in_try(level, parent)) break;
11663 return strip_assignment(def);
11665 } while (is_tail(node, parent));
11668 if (compressor.option("sequences")) {
11669 var seq = self.lift_sequences(compressor);
11670 if (seq !== self) return seq.optimize(compressor);
11672 if (compressor.option("assignments")) {
11673 if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
11674 // x = expr1 OP expr2
11675 if (self.right.left instanceof AST_SymbolRef
11676 && self.right.left.name == self.left.name
11677 && ASSIGN_OPS[self.right.operator]) {
11678 // x = x - 2 ---> x -= 2
11679 return make_node(AST_Assign, self, {
11680 operator: self.right.operator + "=",
11682 right: self.right.right,
11685 if (self.right.right instanceof AST_SymbolRef
11686 && self.right.right.name == self.left.name
11687 && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
11688 && !self.right.left.has_side_effects(compressor)) {
11689 // x = 2 & x ---> x &= 2
11690 return make_node(AST_Assign, self, {
11691 operator: self.right.operator + "=",
11693 right: self.right.left,
11697 if ((self.operator == "-=" || self.operator == "+="
11698 && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
11699 && self.right instanceof AST_Number
11700 && self.right.value == 1) {
11701 var op = self.operator.slice(0, -1);
11702 return make_node(AST_UnaryPrefix, self, {
11704 expression: self.left
11708 return try_evaluate(compressor, self);
11710 function is_tail(node, parent) {
11711 if (parent instanceof AST_Binary) {
11712 return parent.right === node || parent.right.is_constant_expression(scope);
11714 if (parent instanceof AST_Conditional) {
11715 return parent.condition !== node
11716 || parent.consequent.is_constant_expression(scope)
11717 && parent.alternative.is_constant_expression(scope);
11719 if (parent instanceof AST_Sequence) {
11720 var exprs = parent.expressions;
11721 var stop = exprs.indexOf(node);
11722 if (stop < 0) return false;
11723 for (var i = exprs.length; --i > stop;) {
11724 if (!exprs[i].is_constant_expression(scope)) return false;
11728 if (parent instanceof AST_UnaryPrefix) return true;
11731 function is_tail_block(stat, parent) {
11732 if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat);
11733 if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat);
11734 if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat);
11735 if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat;
11736 if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
11739 function in_try(level, node) {
11740 var right = self.right;
11741 self.right = make_node(AST_Null, right);
11742 var may_throw = node.may_throw(compressor);
11743 self.right = right;
11744 for (var parent; parent = compressor.parent(level++); node = parent) {
11745 if (parent === scope) return false;
11746 if (parent instanceof AST_Try) {
11747 if (parent.bfinally && parent.bfinally !== node) return true;
11748 if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
11753 function strip_assignment(def) {
11754 if (def) def.fixed = false;
11755 return (self.operator != "=" ? make_node(AST_Binary, self, {
11756 operator: self.operator.slice(0, -1),
11759 }) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
11763 OPT(AST_Conditional, function(self, compressor) {
11764 if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
11765 var expressions = self.condition.expressions.slice();
11766 self.condition = expressions.pop();
11767 expressions.push(self);
11768 return make_sequence(self, expressions);
11770 if (!compressor.option("conditionals")) return self;
11771 var condition = self.condition;
11772 if (compressor.option("booleans") && !condition.has_side_effects(compressor)) {
11773 mark_duplicate_condition(compressor, condition);
11775 condition = fuzzy_eval(compressor, condition);
11777 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
11778 return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
11779 } else if (!(condition instanceof AST_Node)) {
11780 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
11781 return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
11783 var negated = condition.negate(compressor, first_in_statement(compressor));
11784 if (best_of(compressor, condition, negated) === negated) {
11785 self = make_node(AST_Conditional, self, {
11786 condition: negated,
11787 consequent: self.alternative,
11788 alternative: self.consequent
11790 negated = condition;
11791 condition = self.condition;
11793 var consequent = self.consequent;
11794 var alternative = self.alternative;
11795 if (repeatable(compressor, condition)) {
11796 // x ? x : y ---> x || y
11797 if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
11800 right: alternative,
11801 }).optimize(compressor);
11802 // x ? y : x ---> x && y
11803 if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
11807 }).optimize(compressor);
11809 // if (foo) exp = something; else exp = something_else;
11812 // exp = foo ? something : something_else;
11813 var seq_tail = consequent.tail_node();
11814 if (seq_tail instanceof AST_Assign) {
11815 var is_eq = seq_tail.operator == "=";
11816 var alt_tail = is_eq ? alternative.tail_node() : alternative;
11817 if ((is_eq || consequent === seq_tail)
11818 && alt_tail instanceof AST_Assign
11819 && seq_tail.operator == alt_tail.operator
11820 && seq_tail.left.equivalent_to(alt_tail.left)
11821 && (is_eq && seq_tail.left instanceof AST_SymbolRef
11822 || !condition.has_side_effects(compressor)
11823 && can_shift_lhs_of_tail(consequent)
11824 && can_shift_lhs_of_tail(alternative))) {
11825 return make_node(AST_Assign, self, {
11826 operator: seq_tail.operator,
11827 left: seq_tail.left,
11828 right: make_node(AST_Conditional, self, {
11829 condition: condition,
11830 consequent: pop_lhs(consequent),
11831 alternative: pop_lhs(alternative)
11836 // x ? y : y ---> x, y
11837 if (consequent.equivalent_to(alternative)) return make_sequence(self, [
11840 ]).optimize(compressor);
11841 // x ? y.p : z.p ---> (x ? y : z).p
11842 // x ? y(a) : z(a) ---> (x ? y : z)(a)
11843 // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
11844 var combined = combine_tail(consequent, alternative, true);
11845 if (combined) return combined;
11846 // x ? y(a) : y(b) ---> y(x ? a : b)
11848 if (consequent instanceof AST_Call
11849 && alternative.TYPE == consequent.TYPE
11850 && (arg_index = arg_diff(consequent, alternative)) >= 0
11851 && consequent.expression.equivalent_to(alternative.expression)
11852 && !condition.has_side_effects(compressor)
11853 && !consequent.expression.has_side_effects(compressor)) {
11854 var node = consequent.clone();
11855 var arg = consequent.args[arg_index];
11856 node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, {
11857 expression: make_node(AST_Conditional, self, {
11858 condition: condition,
11859 consequent: arg.expression,
11860 alternative: alternative.args[arg_index].expression,
11862 }) : make_node(AST_Conditional, self, {
11863 condition: condition,
11865 alternative: alternative.args[arg_index],
11869 // x ? (y ? a : b) : b ---> x && y ? a : b
11870 if (consequent instanceof AST_Conditional
11871 && consequent.alternative.equivalent_to(alternative)) {
11872 return make_node(AST_Conditional, self, {
11873 condition: make_node(AST_Binary, self, {
11876 right: consequent.condition
11878 consequent: consequent.consequent,
11879 alternative: alternative
11882 // x ? (y ? a : b) : a ---> !x || y ? a : b
11883 if (consequent instanceof AST_Conditional
11884 && consequent.consequent.equivalent_to(alternative)) {
11885 return make_node(AST_Conditional, self, {
11886 condition: make_node(AST_Binary, self, {
11889 right: consequent.condition
11891 consequent: alternative,
11892 alternative: consequent.alternative
11895 // x ? a : (y ? a : b) ---> x || y ? a : b
11896 if (alternative instanceof AST_Conditional
11897 && consequent.equivalent_to(alternative.consequent)) {
11898 return make_node(AST_Conditional, self, {
11899 condition: make_node(AST_Binary, self, {
11902 right: alternative.condition
11904 consequent: consequent,
11905 alternative: alternative.alternative
11908 // x ? b : (y ? a : b) ---> !x && y ? a : b
11909 if (alternative instanceof AST_Conditional
11910 && consequent.equivalent_to(alternative.alternative)) {
11911 return make_node(AST_Conditional, self, {
11912 condition: make_node(AST_Binary, self, {
11915 right: alternative.condition
11917 consequent: alternative.consequent,
11918 alternative: consequent
11921 // x ? (a, c) : (b, c) ---> x ? a : b, c
11922 if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
11923 && consequent.tail_node().equivalent_to(alternative.tail_node())) {
11924 return make_sequence(self, [
11925 make_node(AST_Conditional, self, {
11926 condition: condition,
11927 consequent: pop_seq(consequent),
11928 alternative: pop_seq(alternative)
11930 consequent.tail_node()
11931 ]).optimize(compressor);
11933 // x ? y && a : a ---> (!x || y) && a
11934 if (consequent instanceof AST_Binary
11935 && consequent.operator == "&&"
11936 && consequent.right.equivalent_to(alternative)) {
11937 return make_node(AST_Binary, self, {
11939 left: make_node(AST_Binary, self, {
11942 right: consequent.left
11945 }).optimize(compressor);
11947 // x ? y || a : a ---> x && y || a
11948 if (consequent instanceof AST_Binary
11949 && consequent.operator == "||"
11950 && consequent.right.equivalent_to(alternative)) {
11951 return make_node(AST_Binary, self, {
11953 left: make_node(AST_Binary, self, {
11956 right: consequent.left
11959 }).optimize(compressor);
11961 // x ? a : y && a ---> (x || y) && a
11962 if (alternative instanceof AST_Binary
11963 && alternative.operator == "&&"
11964 && alternative.right.equivalent_to(consequent)) {
11965 return make_node(AST_Binary, self, {
11967 left: make_node(AST_Binary, self, {
11970 right: alternative.left
11973 }).optimize(compressor);
11975 // x ? a : y || a ---> !x && y || a
11976 if (alternative instanceof AST_Binary
11977 && alternative.operator == "||"
11978 && alternative.right.equivalent_to(consequent)) {
11979 return make_node(AST_Binary, self, {
11981 left: make_node(AST_Binary, self, {
11984 right: alternative.left
11987 }).optimize(compressor);
11989 var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
11990 if (is_true(consequent)) {
11991 if (is_false(alternative)) {
11992 // c ? true : false ---> !!c
11993 return booleanize(condition);
11995 // c ? true : x ---> !!c || x
11996 return make_node(AST_Binary, self, {
11998 left: booleanize(condition),
12002 if (is_false(consequent)) {
12003 if (is_true(alternative)) {
12004 // c ? false : true ---> !c
12005 return booleanize(condition.negate(compressor));
12007 // c ? false : x ---> !c && x
12008 return make_node(AST_Binary, self, {
12010 left: booleanize(condition.negate(compressor)),
12014 if (is_true(alternative)) {
12015 // c ? x : true ---> !c || x
12016 return make_node(AST_Binary, self, {
12018 left: booleanize(condition.negate(compressor)),
12022 if (is_false(alternative)) {
12023 // c ? x : false ---> !!c && x
12024 return make_node(AST_Binary, self, {
12026 left: booleanize(condition),
12030 if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
12033 function booleanize(node) {
12034 if (node.is_boolean(compressor)) return node;
12036 return make_node(AST_UnaryPrefix, node, {
12038 expression: node.negate(compressor)
12043 function is_true(node) {
12044 return node instanceof AST_True
12046 && node instanceof AST_Constant
12048 || (node instanceof AST_UnaryPrefix
12049 && node.operator == "!"
12050 && node.expression instanceof AST_Constant
12051 && !node.expression.value);
12053 // AST_False or !1 or void 0
12054 function is_false(node) {
12055 return node instanceof AST_False
12057 && (node instanceof AST_Constant
12059 || node instanceof AST_UnaryPrefix
12060 && node.operator == "void"
12061 && !node.expression.has_side_effects(compressor))
12062 || (node instanceof AST_UnaryPrefix
12063 && node.operator == "!"
12064 && node.expression instanceof AST_Constant
12065 && node.expression.value);
12068 function arg_diff(consequent, alternative) {
12069 var a = consequent.args;
12070 var b = alternative.args;
12071 var len = a.length;
12072 if (len != b.length) return -2;
12073 for (var i = 0; i < len; i++) {
12074 if (!a[i].equivalent_to(b[i])) {
12075 if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
12076 for (var j = i + 1; j < len; j++) {
12077 if (!a[j].equivalent_to(b[j])) return -2;
12085 function is_tail_equivalent(consequent, alternative) {
12086 if (consequent.TYPE != alternative.TYPE) return;
12087 if (consequent.optional != alternative.optional) return;
12088 if (consequent instanceof AST_Call) {
12089 if (arg_diff(consequent, alternative) != -1) return;
12090 return consequent.TYPE != "Call"
12091 || !(consequent.expression instanceof AST_PropAccess
12092 || alternative.expression instanceof AST_PropAccess)
12093 || is_tail_equivalent(consequent.expression, alternative.expression);
12095 if (!(consequent instanceof AST_PropAccess)) return;
12096 var p = consequent.property;
12097 var q = alternative.property;
12098 return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
12099 && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
12102 function combine_tail(consequent, alternative, top) {
12103 if (!is_tail_equivalent(consequent, alternative)) return !top && make_node(AST_Conditional, self, {
12104 condition: condition,
12105 consequent: consequent,
12106 alternative: alternative,
12108 var node = consequent.clone();
12109 node.expression = combine_tail(consequent.expression, alternative.expression);
12113 function can_shift_lhs_of_tail(node) {
12114 return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
12115 return !expr.has_side_effects(compressor);
12119 function pop_lhs(node) {
12120 if (!(node instanceof AST_Sequence)) return node.right;
12121 var exprs = node.expressions.slice();
12122 exprs.push(exprs.pop().right);
12123 return make_sequence(node, exprs);
12126 function pop_seq(node) {
12127 if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
12130 return make_sequence(node, node.expressions.slice(0, -1));
12134 OPT(AST_Boolean, function(self, compressor) {
12135 if (!compressor.option("booleans")) return self;
12136 if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
12139 var p = compressor.parent();
12140 if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
12141 AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
12142 operator : p.operator,
12143 value : self.value,
12144 file : p.start.file,
12145 line : p.start.line,
12148 return make_node(AST_Number, self, {
12152 return make_node(AST_UnaryPrefix, self, {
12154 expression: make_node(AST_Number, self, {
12155 value: 1 - self.value
12160 OPT(AST_Spread, function(self, compressor) {
12161 var exp = self.expression;
12162 if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
12163 return List.splice(exp.elements.map(function(node) {
12164 return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
12170 function safe_to_flatten(value, compressor) {
12171 if (!value) return false;
12172 var parent = compressor.parent();
12173 if (parent.TYPE != "Call") return true;
12174 if (parent.expression !== compressor.self()) return true;
12175 if (value instanceof AST_SymbolRef) {
12176 value = value.fixed_value();
12177 if (!value) return false;
12179 return value instanceof AST_Lambda && !value.contains_this();
12182 OPT(AST_Sub, function(self, compressor) {
12183 var expr = self.expression;
12184 var prop = self.property;
12185 var terminated = trim_optional_chain(self, compressor);
12186 if (terminated) return terminated;
12187 if (compressor.option("properties")) {
12188 var key = prop.evaluate(compressor);
12189 if (key !== prop) {
12190 if (typeof key == "string") {
12191 if (key == "undefined") {
12194 var value = parseFloat(key);
12195 if (value.toString() == key) {
12200 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
12201 var property = "" + key;
12202 if (is_identifier_string(property)
12203 && property.length <= prop.print_to_string().length + 1) {
12204 return make_node(AST_Dot, self, {
12205 optional: self.optional,
12207 property: property,
12208 }).optimize(compressor);
12212 var parent = compressor.parent();
12213 var assigned = is_lhs(compressor.self(), parent);
12214 var def, fn, fn_parent, index;
12215 if (compressor.option("arguments")
12216 && expr instanceof AST_SymbolRef
12217 && is_arguments(def = expr.definition())
12219 && prop instanceof AST_Number
12220 && Math.floor(index = prop.value) == index
12221 && (fn = def.scope) === find_lambda()
12222 && fn.uses_arguments < (assigned ? 2 : 3)) {
12223 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
12224 if (!def.deleted) def.deleted = [];
12225 def.deleted[index] = true;
12227 var argname = fn.argnames[index];
12228 if (def.deleted && def.deleted[index]) {
12230 } else if (argname) {
12232 if (!(argname instanceof AST_SymbolFunarg)
12233 || argname.name == "await"
12234 || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) {
12236 } else if (compressor.has_directive("use strict")
12239 || !(fn_parent instanceof AST_Call
12240 && index < fn_parent.args.length
12241 && all(fn_parent.args.slice(0, index + 1), function(arg) {
12242 return !(arg instanceof AST_Spread);
12244 || !all(fn.argnames, function(argname) {
12245 return argname instanceof AST_SymbolFunarg;
12247 if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
12249 } else if ((assigned || !has_reassigned())
12250 && index < fn.argnames.length + 5
12251 && compressor.drop_fargs(fn, fn_parent)
12253 while (index >= fn.argnames.length) {
12254 argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
12255 fn.argnames.push(argname);
12258 if (argname && find_if(function(node) {
12259 return node.name === argname.name;
12260 }, fn.argnames) === argname) {
12261 if (assigned) def.reassigned--;
12262 var sym = make_node(AST_SymbolRef, self, argname);
12264 delete argname.__unused;
12268 if (assigned) return self;
12269 if (compressor.option("sequences")
12270 && parent.TYPE != "Call"
12271 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12272 var seq = lift_sequence_in_expression(self, compressor);
12273 if (seq !== self) return seq.optimize(compressor);
12275 if (key !== prop) {
12276 var sub = self.flatten_object(property, compressor);
12278 expr = self.expression = sub.expression;
12279 prop = self.property = sub.property;
12283 if (compressor.option("properties")
12284 && compressor.option("side_effects")
12285 && prop instanceof AST_Number
12286 && expr instanceof AST_Array
12287 && all(elements = expr.elements, function(value) {
12288 return !(value instanceof AST_Spread);
12290 var index = prop.value;
12291 var retValue = elements[index];
12292 if (safe_to_flatten(retValue, compressor)) {
12293 var is_hole = retValue instanceof AST_Hole;
12294 var flatten = !is_hole;
12296 for (var i = elements.length; --i > index;) {
12297 var value = elements[i].drop_side_effect_free(compressor);
12299 values.unshift(value);
12300 if (flatten && value.has_side_effects(compressor)) flatten = false;
12303 if (!flatten) values.unshift(retValue);
12305 var value = elements[i].drop_side_effect_free(compressor);
12307 values.unshift(value);
12308 } else if (is_hole) {
12309 values.unshift(make_node(AST_Hole, elements[i]));
12315 values.push(retValue);
12316 return make_sequence(self, values).optimize(compressor);
12317 } else return make_node(AST_Sub, self, {
12318 expression: make_node(AST_Array, expr, { elements: values }),
12319 property: make_node(AST_Number, prop, { value: index }),
12323 return try_evaluate(compressor, self);
12325 function find_lambda() {
12327 while (p = compressor.parent(i++)) {
12328 if (p instanceof AST_Lambda) {
12329 if (p instanceof AST_Accessor) return;
12330 if (is_arrow(p)) continue;
12331 fn_parent = compressor.parent(i);
12337 function has_reassigned() {
12338 return !compressor.option("reduce_vars") || def.reassigned;
12342 AST_Arrow.DEFMETHOD("contains_super", return_false);
12343 AST_AsyncArrow.DEFMETHOD("contains_super", return_false);
12344 AST_Lambda.DEFMETHOD("contains_super", function() {
12347 self.walk(new TreeWalker(function(node) {
12348 if (result) return true;
12349 if (node instanceof AST_Super) return result = true;
12350 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12354 AST_LambdaDefinition.DEFMETHOD("contains_super", return_false);
12355 AST_Scope.DEFMETHOD("contains_super", return_false);
12357 AST_Arrow.DEFMETHOD("contains_this", return_false);
12358 AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
12359 AST_Node.DEFMETHOD("contains_this", function() {
12362 self.walk(new TreeWalker(function(node) {
12363 if (result) return true;
12364 if (node instanceof AST_This) return result = true;
12365 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12370 function can_hoist_property(prop) {
12371 return prop instanceof AST_ObjectKeyVal
12372 && typeof prop.key == "string"
12373 && !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
12376 AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
12377 if (!compressor.option("properties")) return;
12378 if (key === "__proto__") return;
12379 var expr = this.expression;
12380 if (expr instanceof AST_Object) {
12381 var props = expr.properties;
12382 for (var i = props.length; --i >= 0;) {
12383 var prop = props[i];
12384 if (prop.key !== key) continue;
12385 if (!all(props, can_hoist_property)) return;
12386 if (!safe_to_flatten(prop.value, compressor)) return;
12387 var scope, values = [];
12388 for (var j = 0; j < props.length; j++) {
12389 var value = props[j].value;
12390 if (props[j] instanceof AST_ObjectMethod) {
12391 var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
12393 if (!scope) scope = compressor.find_parent(AST_Scope);
12394 var avoid = avoid_await_yield(scope);
12395 value.each_argname(function(argname) {
12396 if (avoid[argname.name]) arrow = false;
12401 ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow;
12402 } else if (i === j && !(compressor.parent() instanceof AST_Call)) {
12407 value = make_node(ctor, value, value);
12409 values.push(value);
12411 return make_node(AST_Sub, this, {
12412 expression: make_node(AST_Array, expr, { elements: values }),
12413 property: make_node(AST_Number, this, { value: i }),
12419 OPT(AST_Dot, function(self, compressor) {
12420 if (self.property == "arguments" || self.property == "caller") {
12421 AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
12422 prop: self.property,
12423 file: self.start.file,
12424 line: self.start.line,
12425 col: self.start.col,
12428 var parent = compressor.parent();
12429 if (is_lhs(compressor.self(), parent)) return self;
12430 var terminated = trim_optional_chain(self, compressor);
12431 if (terminated) return terminated;
12432 if (compressor.option("sequences")
12433 && parent.TYPE != "Call"
12434 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12435 var seq = lift_sequence_in_expression(self, compressor);
12436 if (seq !== self) return seq.optimize(compressor);
12438 if (compressor.option("unsafe_proto")
12439 && self.expression instanceof AST_Dot
12440 && self.expression.property == "prototype") {
12441 var exp = self.expression.expression;
12442 if (is_undeclared_ref(exp)) switch (exp.name) {
12444 self.expression = make_node(AST_Array, self.expression, {
12449 self.expression = make_node(AST_Function, self.expression, {
12452 }).init_vars(exp.scope);
12455 self.expression = make_node(AST_Number, self.expression, {
12460 self.expression = make_node(AST_Object, self.expression, {
12465 self.expression = make_node(AST_RegExp, self.expression, {
12470 self.expression = make_node(AST_String, self.expression, {
12476 var sub = self.flatten_object(self.property, compressor);
12477 if (sub) return sub.optimize(compressor);
12478 return try_evaluate(compressor, self);
12481 OPT(AST_DestructuredArray, function(self, compressor) {
12482 if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) {
12483 return make_node(AST_DestructuredArray, self, {
12484 elements: self.elements.concat(self.rest.elements),
12485 rest: self.rest.rest,
12491 OPT(AST_DestructuredKeyVal, function(self, compressor) {
12492 if (compressor.option("objects")) {
12493 var key = self.key;
12494 if (key instanceof AST_Node) {
12495 key = key.evaluate(compressor);
12496 if (key !== self.key) self.key = "" + key;
12502 OPT(AST_Object, function(self, compressor) {
12503 if (!compressor.option("objects")) return self;
12504 var changed = false;
12506 var generated = false;
12507 var keep_duplicate = compressor.has_directive("use strict");
12508 var keys = new Dictionary();
12510 self.properties.forEach(function(prop) {
12511 if (!(prop instanceof AST_Spread)) return process(prop);
12513 var exp = prop.expression;
12514 if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
12515 if (prop instanceof AST_ObjectGetter) return false;
12516 if (prop instanceof AST_Spread) return false;
12517 if (prop.key !== "__proto__") return true;
12518 if (prop instanceof AST_ObjectSetter) return true;
12519 return !prop.value.has_side_effects(compressor);
12522 exp.properties.forEach(function(prop) {
12523 var key = prop.key;
12524 var setter = prop instanceof AST_ObjectSetter;
12525 if (key === "__proto__") {
12526 if (!setter) return;
12527 key = make_node_from_constant(key, prop);
12529 process(setter ? make_node(AST_ObjectKeyVal, prop, {
12531 value: make_node(AST_Undefined, prop).optimize(compressor),
12541 if (!changed) return self;
12542 if (found && generated && values.length == 1) {
12543 var value = values[0];
12544 if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
12545 value.key = "" + value.key.value;
12548 return make_node(AST_Object, self, { properties: values });
12551 keys.each(function(props) {
12552 if (props.length == 1) return values.push(props[0]);
12554 var tail = keep_duplicate && !generated && props.pop();
12555 values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
12557 value: make_sequence(self, props.map(function(prop) {
12561 if (tail) values.push(tail);
12563 keys = new Dictionary();
12566 function process(prop) {
12567 var key = prop.key;
12568 if (key instanceof AST_Node) {
12570 key = key.evaluate(compressor);
12571 if (key === prop.key || key === "__proto__") {
12574 key = prop.key = "" + key;
12577 if (can_hoist_property(prop)) {
12578 if (prop.value.has_side_effects(compressor)) flush();
12579 keys.add(key, prop);
12584 if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
12586 if (keys.has(key)) prop = keys.get(key)[0];
12587 prop.key = make_node(AST_Number, prop, { value: +key });
12592 OPT(AST_Return, function(self, compressor) {
12593 if (compressor.option("side_effects")
12595 && is_undefined(self.value, compressor)
12596 && !in_async_generator(compressor.find_parent(AST_Scope))) {
12601 })(function(node, optimizer) {
12602 node.DEFMETHOD("optimize", function(compressor) {
12604 if (self._optimized) return self;
12605 if (compressor.has_directive("use asm")) return self;
12606 var opt = optimizer(self, compressor);
12607 opt._optimized = true;