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 if (value.has_side_effects(compressor) && sym.match_symbol(function(node) {
7215 return node instanceof AST_PropAccess;
7217 value = make_node(AST_Sub, node, {
7219 property: make_node(AST_Number, node, { value: 0 }),
7223 fill_holes(node, elements);
7224 node.elements = elements;
7227 if (node instanceof AST_DestructuredObject) {
7228 var save_drop = drop;
7229 var save_value = value;
7230 if (value instanceof AST_SymbolRef) {
7232 value = value.fixed_value();
7234 var prop_keys, prop_map;
7235 if (value instanceof AST_Object) {
7237 prop_map = Object.create(null);
7238 value.properties.forEach(function(prop, index) {
7239 if (prop instanceof AST_Spread) return prop_map = false;
7241 if (key instanceof AST_Node) key = key.evaluate(compressor, true);
7242 if (key instanceof AST_Node) {
7244 } else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
7245 prop_map[key] = prop;
7247 prop_keys[index] = key;
7252 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7254 var can_drop = Object.create(null);
7255 var drop_keys = drop && Object.create(null);
7256 var properties = [];
7257 node.properties.map(function(prop) {
7259 if (key instanceof AST_Node) {
7260 prop.key = key = key.transform(tt);
7261 key = key.evaluate(compressor, true);
7263 if (key instanceof AST_Node) {
7266 can_drop[key] = !(key in can_drop);
7269 }).forEach(function(key, index) {
7270 var prop = node.properties[index], trimmed;
7271 if (key instanceof AST_Node) {
7274 trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
7276 drop = drop_keys && can_drop[key];
7277 var mapped = prop_map && prop_map[key];
7279 value = mapped.value;
7280 if (value instanceof AST_Accessor) value = false;
7284 trimmed = prop.value.transform(trimmer);
7286 if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
7287 if (drop_keys && !(key in drop_keys)) {
7289 drop_keys[key] = mapped;
7290 if (value === null) {
7291 prop_map[key] = retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
7293 value: make_node(AST_Number, mapped, { value: 0 }),
7297 drop_keys[key] = true;
7300 } else if (drop_keys) {
7301 drop_keys[key] = false;
7303 if (value) mapped.value = value;
7306 prop.value = trimmed;
7307 properties.push(prop);
7312 if (drop_keys && prop_keys) value.properties = List(value.properties, function(prop, index) {
7313 if (prop instanceof AST_Spread) return prop;
7314 var key = prop_keys[index];
7315 if (key instanceof AST_Node) return prop;
7316 if (key in drop_keys) {
7317 var mapped = drop_keys[key];
7318 if (!mapped) return prop;
7319 if (mapped === prop) return prop_map[key] || List.skip;
7320 } else if (node.rest) {
7323 var trimmed = prop.value.drop_side_effect_free(compressor);
7325 prop.value = trimmed;
7328 return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, {
7330 value: make_node(AST_Number, prop, { value: 0 }),
7333 if (value && !node.rest) switch (properties.length) {
7335 if (value.may_throw_on_access(compressor, true)) break;
7336 if (drop) value = value.drop_side_effect_free(compressor);
7340 var prop = properties[0];
7341 if (prop.key instanceof AST_Node) break;
7342 if (prop.value.has_side_effects(compressor)) break;
7343 if (value.has_side_effects(compressor) && prop.value.match_symbol(function(node) {
7344 return node instanceof AST_PropAccess;
7346 value = make_node(AST_Sub, node, {
7348 property: make_node_from_constant(prop.key, prop),
7352 node.properties = properties;
7355 if (node instanceof AST_Hole) {
7358 node = process(node);
7360 if (!node && drop && value) value = value.drop_side_effect_free(compressor);
7364 name: node.transform(trimmer),
7368 function retain_key(prop) {
7369 return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor);
7372 function retain_lhs(node) {
7373 if (node instanceof AST_DefaultValue) return retain_lhs(node.name);
7374 if (node instanceof AST_Destructured) {
7375 if (value === null) {
7376 value = make_node(AST_Number, node, { value: 0 });
7377 } else if (value && (value.tail_node().write_only === true
7378 || value.may_throw_on_access(compressor, true))) {
7379 value = make_node(AST_Array, node, {
7380 elements: value instanceof AST_Sequence ? value.expressions : [ value ],
7383 return make_node(AST_DestructuredObject, node, { properties: [] });
7385 node.__unused = null;
7391 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
7392 if (compressor.has_directive("use asm")) return;
7393 var hoist_funs = compressor.option("hoist_funs");
7394 var hoist_vars = compressor.option("hoist_vars");
7397 // let's count var_decl first, we seem to waste a lot of
7398 // space if we hoist `var` when there's only one.
7400 self.walk(new TreeWalker(function(node) {
7401 if (var_decl > 1) return true;
7402 if (node instanceof AST_ExportDeclaration) return true;
7403 if (node instanceof AST_Scope && node !== self) return true;
7404 if (node instanceof AST_Var) {
7409 if (var_decl <= 1) hoist_vars = false;
7411 if (!hoist_funs && !hoist_vars) return;
7412 var consts = Object.create(null);
7415 var vars = new Dictionary(), vars_found = 0;
7416 var tt = new TreeTransformer(function(node, descend, in_list) {
7417 if (node === self) return;
7418 if (node instanceof AST_Directive) {
7420 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7422 if (node instanceof AST_LambdaDefinition) {
7423 if (!hoist_funs) return node;
7424 var p = tt.parent();
7425 if (p instanceof AST_ExportDeclaration) return node;
7426 if (p instanceof AST_ExportDefault) return node;
7427 if (p !== self && compressor.has_directive("use strict")) return node;
7429 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7431 if (node instanceof AST_Var) {
7432 if (!hoist_vars) return node;
7433 var p = tt.parent();
7434 if (p instanceof AST_ExportDeclaration) return node;
7435 if (!all(node.definitions, function(defn) {
7436 var sym = defn.name;
7437 return sym instanceof AST_SymbolVar
7438 && !consts[sym.name]
7439 && self.find_variable(sym.name) === sym.definition();
7441 node.definitions.forEach(function(def) {
7442 vars.set(def.name.name, def);
7445 var seq = node.to_assignments();
7446 if (p instanceof AST_ForEnumeration && p.init === node) {
7447 if (seq) return seq;
7448 var def = node.definitions[0].name;
7449 return make_node(AST_SymbolRef, def, def);
7451 if (p instanceof AST_For && p.init === node) return seq;
7452 if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7453 return make_node(AST_SimpleStatement, node, { body: seq });
7455 if (node instanceof AST_Scope) return node;
7456 if (node instanceof AST_SymbolConst) {
7457 consts[node.name] = true;
7462 if (vars_found > 0) {
7463 // collect only vars which don't show up in self's arguments list
7465 if (self instanceof AST_Lambda) self.each_argname(function(argname) {
7466 vars.del(argname.name);
7468 vars.each(function(def, name) {
7472 vars.set(name, def);
7474 if (defs.length > 0) {
7475 // try to merge in assignments
7476 insert_vars(self.body);
7477 defs = make_node(AST_Var, self, { definitions: defs });
7481 self.body = dirs.concat(hoisted, self.body);
7483 function insert_vars(body) {
7484 while (body.length) {
7486 if (stat instanceof AST_SimpleStatement) {
7487 var expr = stat.body, sym, assign;
7488 if (expr instanceof AST_Assign
7489 && expr.operator == "="
7490 && (sym = expr.left) instanceof AST_Symbol
7491 && vars.has(sym.name)) {
7492 var def = vars.get(sym.name);
7493 if (def.value) break;
7494 var value = expr.right;
7495 if (value instanceof AST_Sequence) value = value.clone();
7502 if (expr instanceof AST_Sequence
7503 && (assign = expr.expressions[0]) instanceof AST_Assign
7504 && assign.operator == "="
7505 && (sym = assign.left) instanceof AST_Symbol
7506 && vars.has(sym.name)) {
7507 var def = vars.get(sym.name);
7508 if (def.value) break;
7509 def.value = assign.right;
7512 stat.body = make_sequence(expr, expr.expressions.slice(1));
7516 if (stat instanceof AST_EmptyStatement) {
7520 if (stat instanceof AST_BlockStatement && !insert_vars(stat.body)) {
7530 function scan_local_returns(fn, transform) {
7531 fn.walk(new TreeWalker(function(node) {
7532 if (node instanceof AST_Return) {
7536 if (node instanceof AST_Scope && node !== fn) return true;
7540 function map_bool_returns(fn) {
7541 var map = Object.create(null);
7542 scan_local_returns(fn, function(node) {
7543 var value = node.value;
7544 if (value) value = value.tail_node();
7545 if (value instanceof AST_SymbolRef) {
7546 var id = value.definition().id;
7547 map[id] = (map[id] || 0) + 1;
7553 function all_bool(def, bool_returns, compressor) {
7554 return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length - def.replaced
7555 && !compressor.exposed(def);
7558 function process_boolean_returns(fn, compressor) {
7559 scan_local_returns(fn, function(node) {
7560 node.in_bool = true;
7561 var value = node.value;
7563 var ev = fuzzy_eval(compressor, value);
7565 value = value.drop_side_effect_free(compressor);
7566 node.value = value ? make_sequence(node.value, [
7568 make_node(AST_Number, node.value, { value: 0 }),
7570 } else if (!(ev instanceof AST_Node)) {
7571 value = value.drop_side_effect_free(compressor);
7572 node.value = value ? make_sequence(node.value, [
7574 make_node(AST_Number, node.value, { value: 1 }),
7575 ]) : make_node(AST_Number, node.value, { value: 1 });
7581 AST_Scope.DEFMETHOD("process_boolean_returns", noop);
7582 AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
7583 if (!compressor.option("booleans")) return;
7584 var bool_returns = map_bool_returns(this);
7585 if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
7586 if (compressor.parent() instanceof AST_ExportDefault) return;
7587 process_boolean_returns(this, compressor);
7589 AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
7590 if (!compressor.option("booleans")) return;
7591 var bool_returns = map_bool_returns(this);
7592 if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
7593 var parent = compressor.parent();
7594 if (parent instanceof AST_Assign) {
7595 if (parent.operator != "=") return;
7596 var sym = parent.left;
7597 if (!(sym instanceof AST_SymbolRef)) return;
7598 if (!all_bool(sym.definition(), bool_returns, compressor)) return;
7599 } else if (parent instanceof AST_Call && parent.expression !== this) {
7600 var exp = parent.expression;
7601 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
7602 if (!(exp instanceof AST_Lambda)) return;
7603 if (exp.uses_arguments || exp.pinned()) return;
7604 var sym = exp.argnames[parent.args.indexOf(this)];
7605 if (sym instanceof AST_DefaultValue) sym = sym.name;
7606 if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
7607 } else if (parent.TYPE == "Call") {
7609 var in_bool = compressor.in_boolean_context();
7610 compressor.push(this);
7611 if (!in_bool) return;
7613 process_boolean_returns(this, compressor);
7616 AST_BlockScope.DEFMETHOD("var_names", function() {
7617 var var_names = this._var_names;
7619 this._var_names = var_names = Object.create(null);
7620 this.enclosed.forEach(function(def) {
7621 var_names[def.name] = true;
7623 this.variables.each(function(def, name) {
7624 var_names[name] = true;
7630 AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
7631 var scopes = [ this ];
7632 if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
7634 if (member(s, scopes)) return;
7636 push_uniq(scopes, s);
7638 } while (s && s !== this);
7640 prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
7642 for (var i = 0; !all(scopes, function(scope) {
7643 return !scope.var_names()[name];
7644 }); i++) name = prefix + "$" + i;
7645 var sym = make_node(type, orig, {
7649 var def = this.def_variable(sym);
7650 scopes.forEach(function(scope) {
7651 scope.enclosed.push(def);
7652 scope.var_names()[name] = true;
7657 AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
7658 if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
7660 var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
7661 var defs_by_id = Object.create(null);
7662 self.transform(new TreeTransformer(function(node, descend) {
7663 if (node instanceof AST_Assign) {
7664 if (node.operator != "=") return;
7665 if (!node.write_only) return;
7666 if (!can_hoist(node.left, node.right, 1)) return;
7667 descend(node, this);
7668 var defs = new Dictionary();
7669 var assignments = [];
7671 node.right.properties.forEach(function(prop) {
7672 var decl = make_sym(AST_SymbolVar, node.left, prop.key);
7673 decls.push(make_node(AST_VarDef, node, {
7677 var sym = make_node(AST_SymbolRef, node, {
7680 thedef: decl.definition(),
7683 assignments.push(make_node(AST_Assign, node, {
7689 defs.value = node.right;
7690 defs_by_id[node.left.definition().id] = defs;
7691 self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
7694 return make_sequence(node, assignments);
7696 if (node instanceof AST_Scope) return node === self ? undefined : node;
7697 if (node instanceof AST_VarDef) {
7698 if (!can_hoist(node.name, node.value, 0)) return;
7699 descend(node, this);
7700 var defs = new Dictionary();
7702 var decl = node.clone();
7703 decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null;
7704 var_defs.push(decl);
7705 node.value.properties.forEach(function(prop) {
7706 var_defs.push(make_node(AST_VarDef, node, {
7707 name: make_sym(node.name.CTOR, node.name, prop.key),
7711 defs.value = node.value;
7712 defs_by_id[node.name.definition().id] = defs;
7713 return List.splice(var_defs);
7716 function make_sym(type, sym, key) {
7717 var new_var = self.make_var(type, sym, sym.name + "_" + key);
7718 defs.set(key, new_var.definition());
7722 self.transform(new TreeTransformer(function(node, descend) {
7723 if (node instanceof AST_PropAccess) {
7724 if (!(node.expression instanceof AST_SymbolRef)) return;
7725 var defs = defs_by_id[node.expression.definition().id];
7727 if (node.expression.fixed_value() !== defs.value) return;
7728 var def = defs.get(node.get_property());
7729 var sym = make_node(AST_SymbolRef, node, {
7731 scope: node.expression.scope,
7737 if (node instanceof AST_SymbolRef) {
7738 var defs = defs_by_id[node.definition().id];
7740 if (node.fixed_value() !== defs.value) return;
7741 return make_node(AST_Object, node, { properties: [] });
7745 function can_hoist(sym, right, count) {
7746 if (!(sym instanceof AST_Symbol)) return;
7747 var def = sym.definition();
7748 if (def.assignments != count) return;
7749 if (def.references.length - def.replaced == count) return;
7750 if (def.single_use) return;
7751 if (top_retain(def)) return;
7752 if (sym.fixed_value() !== right) return;
7753 var fixed = sym.fixed || def.fixed;
7754 if (fixed.direct_access) return;
7755 if (fixed.escaped && fixed.escaped.depth == 1) return;
7756 return right instanceof AST_Object
7757 && right.properties.length > 0
7758 && all(right.properties, can_hoist_property)
7759 && can_drop_symbol(sym, compressor);
7763 function fn_name_unused(fn, compressor) {
7764 if (!fn.name || !compressor.option("ie")) return true;
7765 var def = fn.name.definition();
7766 if (compressor.exposed(def)) return false;
7767 return all(def.references, function(sym) {
7768 return !(sym instanceof AST_SymbolRef);
7772 // drop_side_effect_free()
7773 // remove side-effect-free parts which only affects return value
7775 // Drop side-effect-free elements from an array of expressions.
7776 // Returns an array of expressions with side-effects or null
7777 // if all elements were dropped. Note: original array may be
7778 // returned if nothing changed.
7779 function trim(nodes, compressor, first_in_statement, spread) {
7780 var len = nodes.length;
7781 var ret = [], changed = false;
7782 for (var i = 0; i < len; i++) {
7783 var node = nodes[i];
7785 if (spread && node instanceof AST_Spread) {
7786 trimmed = spread(node, compressor, first_in_statement);
7788 trimmed = node.drop_side_effect_free(compressor, first_in_statement);
7790 if (trimmed !== node) changed = true;
7793 first_in_statement = false;
7796 return ret.length ? changed ? ret : nodes : null;
7798 function array_spread(node, compressor, first_in_statement) {
7799 var exp = node.expression;
7800 if (!exp.is_string(compressor)) return node;
7801 return exp.drop_side_effect_free(compressor, first_in_statement);
7803 function convert_spread(node) {
7804 return node instanceof AST_Spread ? make_node(AST_Array, node, {
7808 def(AST_Node, return_this);
7809 def(AST_Accessor, return_null);
7810 def(AST_Array, function(compressor, first_in_statement) {
7811 var values = trim(this.elements, compressor, first_in_statement, array_spread);
7812 if (!values) return null;
7813 if (values === this.elements && all(values, function(node) {
7814 return node instanceof AST_Spread;
7816 return make_sequence(this, values.map(convert_spread));
7818 def(AST_Assign, function(compressor) {
7819 var left = this.left;
7820 if (left instanceof AST_PropAccess) {
7821 var expr = left.expression;
7822 if (expr.may_throw_on_access(compressor, true)) return this;
7823 if (compressor.has_directive("use strict") && expr.is_constant()) return this;
7825 if (left.has_side_effects(compressor)) return this;
7826 if (lazy_op[this.operator.slice(0, -1)]) return this;
7827 this.write_only = true;
7828 if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this;
7829 return this.right.drop_side_effect_free(compressor);
7831 def(AST_Await, function(compressor) {
7832 if (!compressor.option("awaits")) return this;
7833 var exp = this.expression;
7834 if (!is_primitive(compressor, exp)) return this;
7835 var node = this.clone();
7836 node.expression = exp.drop_side_effect_free(compressor) || make_node(AST_Number, this, { value: 0 });
7839 def(AST_Binary, function(compressor, first_in_statement) {
7840 var left = this.left;
7841 var right = this.right;
7842 var op = this.operator;
7843 if (op == "in" && !is_object(right)) {
7844 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7845 if (lhs === left) return this;
7846 var node = this.clone();
7847 node.left = lhs || make_node(AST_Number, left, { value: 0 });
7850 var rhs = right.drop_side_effect_free(compressor, first_in_statement);
7851 if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement);
7852 if (lazy_op[op] && rhs.has_side_effects(compressor)) {
7854 if (rhs !== right) {
7855 node = node.clone();
7856 node.right = rhs.drop_side_effect_free(compressor);
7858 if (op == "??") return node;
7859 var negated = make_node(AST_Binary, this, {
7860 operator: op == "&&" ? "||" : "&&",
7861 left: left.negate(compressor, first_in_statement),
7864 return first_in_statement ? best_of_statement(node, negated) : best_of_expression(node, negated);
7866 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7867 if (!lhs) return rhs;
7868 rhs = rhs.drop_side_effect_free(compressor);
7869 if (!rhs) return lhs;
7870 return make_sequence(this, [ lhs, rhs ]);
7872 function drop_returns(compressor, exp) {
7873 var arrow = is_arrow(exp);
7874 var async = is_async(exp);
7875 var drop_body = false;
7876 if (arrow && compressor.option("arrows")) {
7879 } else if (!async || is_primitive(compressor, exp.value)) {
7880 exp.value = exp.value.drop_side_effect_free(compressor);
7882 } else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
7884 var def = exp.name.definition();
7885 drop_body = def.references.length == def.replaced;
7891 exp.process_expression(false, function(node) {
7892 var value = node.value;
7894 if (async && !is_primitive(compressor, value)) return node;
7895 value = value.drop_side_effect_free(compressor, true);
7897 if (!value) return make_node(AST_EmptyStatement, node);
7898 return make_node(AST_SimpleStatement, node, { body: value });
7900 scan_local_returns(exp, function(node) {
7901 var value = node.value;
7903 if (async && !is_primitive(compressor, value)) return;
7904 node.value = value.drop_side_effect_free(compressor);
7908 if (async && compressor.option("awaits")) {
7909 if (drop_body) exp.process_expression("awaits", function(node) {
7910 var body = node.body;
7911 if (body instanceof AST_Await) {
7912 if (is_primitive(compressor, body.expression)) {
7913 body = body.expression.drop_side_effect_free(compressor, true);
7914 if (!body) return make_node(AST_EmptyStatement, node);
7917 } else if (body instanceof AST_Sequence) {
7918 var exprs = body.expressions;
7919 for (var i = exprs.length; --i >= 0;) {
7920 var tail = exprs[i];
7921 if (!(tail instanceof AST_Await)) break;
7922 if (!is_primitive(compressor, tail.expression)) break;
7923 if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
7927 return make_node(AST_EmptyStatement, node);
7929 node.body = exprs[0];
7932 exprs.length = i + 1;
7938 var abort = !drop_body && exp.name || arrow && exp.value && !is_primitive(compressor, exp.value);
7939 var tw = new TreeWalker(function(node) {
7940 if (abort) return true;
7941 if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
7942 if (node instanceof AST_Await) return abort = true;
7943 if (node instanceof AST_ForAwaitOf) return abort = true;
7944 if (node instanceof AST_Return) {
7945 if (node.value && !is_primitive(compressor, node.value)) return abort = true;
7948 if (node instanceof AST_Scope && node !== exp) return true;
7954 case AST_AsyncArrow:
7957 case AST_AsyncFunction:
7958 ctor = AST_Function;
7960 case AST_AsyncGeneratorFunction:
7961 ctor = AST_GeneratorFunction;
7964 return make_node(ctor, exp, exp);
7967 return drop_body && exp.clone();
7969 def(AST_Call, function(compressor, first_in_statement) {
7971 if (self.is_expr_pure(compressor)) {
7972 if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
7973 var args = trim(self.args, compressor, first_in_statement, array_spread);
7974 return args && make_sequence(self, args.map(convert_spread));
7976 var exp = self.expression;
7977 if (self.is_call_pure(compressor)) {
7978 var exprs = self.args.slice();
7979 exprs.unshift(exp.expression);
7980 exprs = trim(exprs, compressor, first_in_statement, array_spread);
7981 return exprs && make_sequence(self, exprs.map(convert_spread));
7983 if (compressor.option("yields") && is_generator(exp)) {
7984 var call = self.clone();
7985 call.expression = make_node(AST_Function, exp, exp);
7986 call.expression.body = [];
7987 var opt = call.transform(compressor);
7988 if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
7990 var dropped = drop_returns(compressor, exp);
7992 // always shallow clone to ensure stripping of negated IIFEs
7993 self = self.clone();
7994 self.expression = dropped;
7995 // avoid extraneous traversal
7996 if (exp._squeezed) self.expression._squeezed = true;
7998 if (self instanceof AST_New) {
8000 if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
8001 if (fn instanceof AST_Lambda) {
8002 if (assign_this_only(fn, compressor)) {
8003 var exprs = self.args.slice();
8005 exprs = trim(exprs, compressor, first_in_statement, array_spread);
8006 return exprs && make_sequence(self, exprs.map(convert_spread));
8008 if (!fn.contains_this()) self = make_node(AST_Call, self, self);
8011 self.call_only = true;
8014 function assign_this_only(fn, compressor) {
8016 var result = all(fn.body, function(stat) {
8017 return !stat.has_side_effects(compressor);
8018 }) && all(fn.argnames, function(argname) {
8019 return !argname.match_symbol(return_false);
8020 }) && !(fn.rest && fn.rest.match_symbol(return_false));
8024 function drop_class(self, compressor, first_in_statement) {
8025 var exprs = [], values = [];
8026 var props = self.properties;
8027 for (var i = 0; i < props.length; i++) {
8028 var prop = props[i];
8029 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8030 if (prop.static && prop.value
8031 && prop instanceof AST_ClassField
8032 && prop.value.has_side_effects(compressor)) {
8033 if (prop.value.contains_this()) return self;
8034 values.push(prop.value);
8037 var base = self.extends;
8039 if (base instanceof AST_SymbolRef) base = base.fixed_value();
8040 base = !safe_for_extends(base);
8041 if (!base) exprs.unshift(self.extends);
8043 exprs = trim(exprs, compressor, first_in_statement);
8044 if (exprs) first_in_statement = false;
8045 values = trim(values, compressor, first_in_statement);
8047 if (!base && !values) return null;
8051 var node = to_class_expr(self, true);
8052 node.properties = [];
8053 if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, {
8054 key: make_sequence(self, exprs),
8055 value: make_node(AST_Function, self, {
8062 if (values) exprs.push(make_node(AST_Call, self, {
8063 expression: make_node(AST_Arrow, self, {
8066 value: make_sequence(self, values),
8067 }).init_vars(self.parent_scope),
8070 return make_sequence(self, exprs);
8072 def(AST_ClassExpression, function(compressor, first_in_statement) {
8074 var name = self.name;
8075 if (name && name.fixed_value() !== self && name.definition().references.length > 0) return self;
8076 return drop_class(self, compressor, first_in_statement);
8078 def(AST_Conditional, function(compressor) {
8079 var consequent = this.consequent.drop_side_effect_free(compressor);
8080 var alternative = this.alternative.drop_side_effect_free(compressor);
8081 if (consequent === this.consequent && alternative === this.alternative) return this;
8083 if (compressor.option("ie")) {
8085 if (consequent instanceof AST_Function) {
8086 exprs.push(consequent);
8089 if (alternative instanceof AST_Function) {
8090 exprs.push(alternative);
8096 node = alternative ? make_node(AST_Binary, this, {
8098 left: this.condition,
8100 }) : this.condition.drop_side_effect_free(compressor);
8101 } else if (!alternative) {
8102 node = make_node(AST_Binary, this, {
8104 left: this.condition,
8108 node = this.clone();
8109 node.consequent = consequent;
8110 node.alternative = alternative;
8112 if (!compressor.option("ie")) return node;
8113 if (node) exprs.push(node);
8114 return exprs.length == 0 ? null : make_sequence(this, exprs);
8116 def(AST_Constant, return_null);
8117 def(AST_DefClass, function(compressor, first_in_statement) {
8118 return drop_class(this, compressor, first_in_statement);
8120 def(AST_Dot, function(compressor, first_in_statement) {
8121 var expr = this.expression;
8122 if (!this.optional && expr.may_throw_on_access(compressor)) return this;
8123 return expr.drop_side_effect_free(compressor, first_in_statement);
8125 def(AST_Function, function(compressor) {
8126 return fn_name_unused(this, compressor) ? null : this;
8128 def(AST_LambdaExpression, return_null);
8129 def(AST_Object, function(compressor, first_in_statement) {
8131 this.properties.forEach(function(prop) {
8132 if (prop instanceof AST_Spread) {
8135 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8136 exprs.push(prop.value);
8139 var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
8140 var exp = node.expression;
8141 return spread_side_effects(exp) ? node : exp.drop_side_effect_free(compressor, first_in_statement);
8143 if (!values) return null;
8144 if (values === exprs && !all(values, function(node) {
8145 return !(node instanceof AST_Spread);
8147 return make_sequence(this, values.map(function(node) {
8148 return node instanceof AST_Spread ? make_node(AST_Object, node, {
8149 properties: [ node ],
8153 def(AST_ObjectIdentity, return_null);
8154 def(AST_Sequence, function(compressor, first_in_statement) {
8155 var expressions = trim(this.expressions, compressor, first_in_statement);
8156 if (!expressions) return null;
8157 var end = expressions.length - 1;
8158 var last = expressions[end];
8159 if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) {
8160 expressions = expressions.slice(0, -1);
8162 last.expression = expressions[end];
8163 expressions[end] = last;
8165 var assign, cond, lhs;
8166 if (compressor.option("conditionals")
8168 && (assign = expressions[end - 1]) instanceof AST_Assign
8169 && assign.operator == "="
8170 && (lhs = assign.left) instanceof AST_SymbolRef
8171 && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) {
8172 assign = assign.clone();
8173 assign.right = cond;
8174 expressions = expressions.slice(0, -2);
8175 expressions.push(assign.drop_side_effect_free(compressor, first_in_statement));
8177 return expressions === this.expressions ? this : make_sequence(this, expressions);
8179 def(AST_Sub, function(compressor, first_in_statement) {
8180 var expr = this.expression;
8181 var prop = this.property;
8182 if (expr.may_throw_on_access(compressor)) {
8183 if (!this.optional) return this;
8184 if (prop.has_side_effects(compressor)) {
8185 prop = prop.drop_side_effect_free(compressor);
8186 if (!prop) return expr.drop_side_effect_free(compressor, first_in_statement);
8187 var node = this.clone();
8188 node.property = prop;
8192 expr = expr.drop_side_effect_free(compressor, first_in_statement);
8193 if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement);
8194 prop = prop.drop_side_effect_free(compressor);
8195 if (!prop) return expr;
8196 return make_sequence(this, [ expr, prop ]);
8198 def(AST_SymbolRef, function(compressor) {
8199 return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
8201 def(AST_Template, function(compressor, first_in_statement) {
8203 if (self.is_expr_pure(compressor)) {
8204 var expressions = self.expressions;
8205 if (expressions.length == 0) return null;
8206 return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
8209 var dropped = drop_returns(compressor, tag);
8211 // always shallow clone to signal internal changes
8212 self = self.clone();
8214 // avoid extraneous traversal
8215 if (tag._squeezed) self.tag._squeezed = true;
8219 def(AST_Unary, function(compressor, first_in_statement) {
8220 var exp = this.expression;
8221 if (unary_side_effects[this.operator]) {
8222 this.write_only = !exp.has_side_effects(compressor);
8225 if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) {
8228 var node = exp.drop_side_effect_free(compressor, first_in_statement);
8229 if (first_in_statement && node && is_iife_call(node)) {
8230 if (node === exp && this.operator == "!") return this;
8231 return node.negate(compressor, first_in_statement);
8235 })(function(node, func) {
8236 node.DEFMETHOD("drop_side_effect_free", func);
8239 OPT(AST_SimpleStatement, function(self, compressor) {
8240 if (compressor.option("side_effects")) {
8241 var body = self.body;
8242 var node = body.drop_side_effect_free(compressor, true);
8244 AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
8245 return make_node(AST_EmptyStatement, self);
8247 if (node !== body) {
8248 return make_node(AST_SimpleStatement, self, { body: node });
8254 OPT(AST_While, function(self, compressor) {
8255 return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
8258 function has_loop_control(loop, parent, type) {
8259 if (!type) type = AST_LoopControl;
8261 var tw = new TreeWalker(function(node) {
8262 if (found || node instanceof AST_Scope) return true;
8263 if (node instanceof type && tw.loopcontrol_target(node) === loop) {
8264 return found = true;
8267 if (parent instanceof AST_LabeledStatement) tw.push(parent);
8273 OPT(AST_Do, function(self, compressor) {
8274 if (!compressor.option("loops")) return self;
8275 var cond = fuzzy_eval(compressor, self.condition);
8276 if (!(cond instanceof AST_Node)) {
8277 if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, {
8278 body: make_node(AST_BlockStatement, self.body, {
8281 make_node(AST_SimpleStatement, self.condition, {
8282 body: self.condition
8286 }).optimize(compressor);
8287 if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, {
8290 make_node(AST_SimpleStatement, self.condition, {
8291 body: self.condition
8294 }).optimize(compressor);
8296 if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
8297 var body = self.body.body;
8298 for (var i = body.length; --i >= 0;) {
8300 if (stat instanceof AST_If
8301 && !stat.alternative
8302 && stat.body instanceof AST_Break
8303 && compressor.loopcontrol_target(stat.body) === self) {
8304 if (has_block_scope_refs(stat.condition)) break;
8305 self.condition = make_node(AST_Binary, self, {
8307 left: stat.condition.negate(compressor),
8308 right: self.condition,
8311 } else if (stat instanceof AST_SimpleStatement) {
8312 if (has_block_scope_refs(stat.body)) break;
8313 self.condition = make_sequence(self, [
8318 } else if (!is_declaration(stat, true)) {
8322 self.body = trim_block(self.body, compressor.parent());
8324 if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
8325 if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
8326 condition: make_sequence(self.condition, [
8330 body: make_node(AST_EmptyStatement, self)
8331 }).optimize(compressor);
8334 function has_block_scope_refs(node) {
8336 node.walk(new TreeWalker(function(node) {
8337 if (found) return true;
8338 if (node instanceof AST_SymbolRef) {
8339 if (!member(node.definition(), self.enclosed)) found = true;
8347 function if_break_in_loop(self, compressor) {
8348 var first = first_statement(self.body);
8349 if (compressor.option("dead_code")
8350 && (first instanceof AST_Break
8351 || first instanceof AST_Continue && external_target(first)
8352 || first instanceof AST_Exit)) {
8354 if (is_statement(self.init)) {
8355 body.push(self.init);
8356 } else if (self.init) {
8357 body.push(make_node(AST_SimpleStatement, self.init, {
8361 var retain = external_target(first) || first instanceof AST_Exit;
8362 if (self.condition && retain) {
8363 body.push(make_node(AST_If, self, {
8364 condition: self.condition,
8368 } else if (self.condition) {
8369 body.push(make_node(AST_SimpleStatement, self.condition, {
8370 body: self.condition
8372 } else if (retain) {
8375 extract_declarations_from_unreachable_code(compressor, self.body, body);
8376 return make_node(AST_BlockStatement, self, {
8380 if (first instanceof AST_If) {
8381 var ab = first_statement(first.body);
8382 if (ab instanceof AST_Break && !external_target(ab)) {
8383 if (self.condition) {
8384 self.condition = make_node(AST_Binary, self.condition, {
8385 left: self.condition,
8387 right: first.condition.negate(compressor),
8390 self.condition = first.condition.negate(compressor);
8392 var body = as_statement_array(first.alternative);
8393 extract_declarations_from_unreachable_code(compressor, first.body, body);
8394 return drop_it(body);
8396 ab = first_statement(first.alternative);
8397 if (ab instanceof AST_Break && !external_target(ab)) {
8398 if (self.condition) {
8399 self.condition = make_node(AST_Binary, self.condition, {
8400 left: self.condition,
8402 right: first.condition,
8405 self.condition = first.condition;
8407 var body = as_statement_array(first.body);
8408 extract_declarations_from_unreachable_code(compressor, first.alternative, body);
8409 return drop_it(body);
8414 function first_statement(body) {
8415 return body instanceof AST_BlockStatement ? body.body[0] : body;
8418 function external_target(node) {
8419 return compressor.loopcontrol_target(node) !== compressor.self();
8422 function drop_it(rest) {
8423 if (self.body instanceof AST_BlockStatement) {
8424 self.body = self.body.clone();
8425 self.body.body = rest.concat(self.body.body.slice(1));
8426 self.body = self.body.transform(compressor);
8428 self.body = make_node(AST_BlockStatement, self.body, {
8430 }).transform(compressor);
8432 return if_break_in_loop(self, compressor);
8436 OPT(AST_For, function(self, compressor) {
8437 if (!compressor.option("loops")) return self;
8438 if (compressor.option("side_effects")) {
8439 if (self.init) self.init = self.init.drop_side_effect_free(compressor);
8440 if (self.step) self.step = self.step.drop_side_effect_free(compressor);
8442 if (self.condition) {
8443 var cond = fuzzy_eval(compressor, self.condition);
8445 if (compressor.option("dead_code")) {
8447 if (is_statement(self.init)) {
8448 body.push(self.init);
8449 } else if (self.init) {
8450 body.push(make_node(AST_SimpleStatement, self.init, { body: self.init }));
8452 body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition }));
8453 extract_declarations_from_unreachable_code(compressor, self.body, body);
8454 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8456 } else if (!(cond instanceof AST_Node)) {
8457 self.body = make_node(AST_BlockStatement, self.body, {
8459 make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8463 self.condition = null;
8466 return if_break_in_loop(self, compressor);
8469 OPT(AST_ForEnumeration, function(self, compressor) {
8470 if (compressor.option("varify") && is_lexical_definition(self.init)) {
8471 var name = self.init.definitions[0].name;
8472 if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
8473 && !name.match_symbol(function(node) {
8474 if (node instanceof AST_SymbolDeclaration) {
8475 var def = node.definition();
8476 return !same_scope(def) || may_overlap(compressor, def);
8479 self.init = to_var(self.init);
8485 function mark_locally_defined(condition, consequent, alternative) {
8486 if (!(condition instanceof AST_Binary)) return;
8487 if (!(condition.left instanceof AST_String)) {
8488 switch (condition.operator) {
8490 mark_locally_defined(condition.left, consequent);
8491 mark_locally_defined(condition.right, consequent);
8494 mark_locally_defined(negate(condition.left), alternative);
8495 mark_locally_defined(negate(condition.right), alternative);
8500 if (!(condition.right instanceof AST_UnaryPrefix)) return;
8501 if (condition.right.operator != "typeof") return;
8502 var sym = condition.right.expression;
8503 if (!is_undeclared_ref(sym)) return;
8505 var undef = condition.left.value == "undefined";
8506 switch (condition.operator) {
8508 body = undef ? alternative : consequent;
8511 body = undef ? consequent : alternative;
8517 var def = sym.definition();
8518 var tw = new TreeWalker(function(node) {
8519 if (node instanceof AST_Scope) {
8520 var parent = tw.parent();
8521 if (parent instanceof AST_Call && parent.expression === node) return;
8524 if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
8528 function negate(node) {
8529 if (!(node instanceof AST_Binary)) return;
8530 switch (node.operator) {
8532 node = node.clone();
8533 node.operator = "!=";
8536 node = node.clone();
8537 node.operator = "==";
8543 function fuzzy_eval(compressor, node, nullish) {
8544 if (node.truthy) return true;
8545 if (node.falsy && !nullish) return false;
8546 if (node.is_truthy()) return true;
8547 return node.evaluate(compressor, true);
8550 function mark_duplicate_condition(compressor, node) {
8553 var negated = false;
8554 var parent = compressor.self();
8555 if (!is_statement(parent)) while (true) {
8557 parent = compressor.parent(level++);
8558 if (parent instanceof AST_Binary) {
8559 var op = parent.operator;
8560 if (!lazy_op[op]) return;
8561 var left = parent.left;
8562 if (left === child) continue;
8563 if (match(left)) switch (op) {
8565 node[negated ? "falsy" : "truthy"] = true;
8569 node[negated ? "truthy" : "falsy"] = true;
8572 } else if (parent instanceof AST_Conditional) {
8573 var cond = parent.condition;
8574 if (cond === child) continue;
8575 if (match(cond)) switch (child) {
8576 case parent.consequent:
8577 node[negated ? "falsy" : "truthy"] = true;
8579 case parent.alternative:
8580 node[negated ? "truthy" : "falsy"] = true;
8583 } else if (parent instanceof AST_Exit) {
8585 } else if (parent instanceof AST_If) {
8587 } else if (parent instanceof AST_Sequence) {
8588 if (parent.expressions[0] === child) continue;
8589 } else if (parent instanceof AST_SimpleStatement) {
8596 parent = compressor.parent(level++);
8597 if (parent instanceof AST_BlockStatement) {
8598 if (parent.body[0] === child) continue;
8599 } else if (parent instanceof AST_If) {
8600 if (match(parent.condition)) switch (child) {
8602 node[negated ? "falsy" : "truthy"] = true;
8604 case parent.alternative:
8605 node[negated ? "truthy" : "falsy"] = true;
8612 function match(cond) {
8613 if (node.equivalent_to(cond)) return true;
8614 if (!(cond instanceof AST_UnaryPrefix)) return false;
8615 if (cond.operator != "!") return false;
8616 if (!node.equivalent_to(cond.expression)) return false;
8622 OPT(AST_If, function(self, compressor) {
8623 if (is_empty(self.alternative)) self.alternative = null;
8625 if (!compressor.option("conditionals")) return self;
8626 if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) {
8627 mark_duplicate_condition(compressor, self.condition);
8629 // if condition can be statically determined, warn and drop
8630 // one of the blocks. note, statically determined implies
8631 // “has no side effects”; also it doesn't work for cases like
8632 // `x && true`, though it probably should.
8633 if (compressor.option("dead_code")) {
8634 var cond = fuzzy_eval(compressor, self.condition);
8636 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
8637 var body = [ make_node(AST_SimpleStatement, self.condition, { body: self.condition }) ];
8638 extract_declarations_from_unreachable_code(compressor, self.body, body);
8639 if (self.alternative) body.push(self.alternative);
8640 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8641 } else if (!(cond instanceof AST_Node)) {
8642 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
8644 make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8647 if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body);
8648 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8651 var negated = self.condition.negate(compressor);
8652 var self_condition_length = self.condition.print_to_string().length;
8653 var negated_length = negated.print_to_string().length;
8654 var negated_is_best = negated_length < self_condition_length;
8655 if (self.alternative && negated_is_best) {
8656 negated_is_best = false; // because we already do the switch here.
8657 // no need to swap values of self_condition_length and negated_length
8658 // here because they are only used in an equality comparison later on.
8659 self.condition = negated;
8660 var tmp = self.body;
8661 self.body = self.alternative || make_node(AST_EmptyStatement, self);
8662 self.alternative = tmp;
8664 var body = [], var_defs = [], refs = [];
8665 var body_exprs = sequencesize(self.body, body, var_defs, refs);
8666 var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
8667 if (body_exprs && alt_exprs) {
8668 if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
8669 if (body_exprs.length == 0) {
8670 body.push(make_node(AST_SimpleStatement, self.condition, {
8671 body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
8673 left : self.condition,
8674 right : make_sequence(self.alternative, alt_exprs)
8675 }).transform(compressor) : self.condition.clone()
8676 }).optimize(compressor));
8677 } else if (alt_exprs.length == 0) {
8678 if (self_condition_length === negated_length && !negated_is_best
8679 && self.condition instanceof AST_Binary && self.condition.operator == "||") {
8680 // although the code length of self.condition and negated are the same,
8681 // negated does not require additional surrounding parentheses.
8682 // see https://github.com/mishoo/UglifyJS/issues/979
8683 negated_is_best = true;
8685 body.push(make_node(AST_SimpleStatement, self, {
8686 body: make_node(AST_Binary, self, {
8687 operator : negated_is_best ? "||" : "&&",
8688 left : negated_is_best ? negated : self.condition,
8689 right : make_sequence(self.body, body_exprs)
8690 }).transform(compressor)
8691 }).optimize(compressor));
8693 body.push(make_node(AST_SimpleStatement, self, {
8694 body: make_node(AST_Conditional, self, {
8695 condition : self.condition,
8696 consequent : make_sequence(self.body, body_exprs),
8697 alternative : make_sequence(self.alternative, alt_exprs)
8699 }).optimize(compressor));
8701 refs.forEach(function(ref) {
8702 ref.definition().references.push(ref);
8704 return make_node(AST_BlockStatement, self, {
8706 }).optimize(compressor);
8708 if (is_empty(self.body)) {
8709 self = make_node(AST_If, self, {
8711 body: self.alternative,
8715 if (self.body instanceof AST_Exit
8716 && self.alternative instanceof AST_Exit
8717 && self.body.TYPE == self.alternative.TYPE) {
8718 var exit = make_node(self.body.CTOR, self, {
8719 value: make_node(AST_Conditional, self, {
8720 condition : self.condition,
8721 consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
8722 alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
8725 if (exit instanceof AST_Return) {
8726 exit.in_bool = self.body.in_bool || self.alternative.in_bool;
8730 if (self.body instanceof AST_If
8731 && !self.body.alternative
8732 && !self.alternative) {
8733 self = make_node(AST_If, self, {
8734 condition: make_node(AST_Binary, self.condition, {
8736 left: self.condition,
8737 right: self.body.condition
8739 body: self.body.body,
8743 if (aborts(self.body)) {
8744 if (self.alternative) {
8745 var alt = self.alternative;
8746 self.alternative = null;
8747 return make_node(AST_BlockStatement, self, {
8749 }).optimize(compressor);
8752 if (aborts(self.alternative)) {
8753 var body = self.body;
8754 self.body = self.alternative;
8755 self.condition = negated_is_best ? negated : self.condition.negate(compressor);
8756 self.alternative = null;
8757 return make_node(AST_BlockStatement, self, {
8758 body: [ self, body ]
8759 }).optimize(compressor);
8761 if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
8764 function sequencesize(stat, defuns, var_defs, refs) {
8765 if (stat == null) return [];
8766 if (stat instanceof AST_BlockStatement) {
8768 for (var i = 0; i < stat.body.length; i++) {
8769 var line = stat.body[i];
8770 if (line instanceof AST_LambdaDefinition) {
8772 } else if (line instanceof AST_EmptyStatement) {
8774 } else if (line instanceof AST_SimpleStatement) {
8775 if (!compressor.option("sequences") && exprs.length > 0) return;
8776 exprs.push(line.body);
8777 } else if (line instanceof AST_Var) {
8778 if (!compressor.option("sequences") && exprs.length > 0) return;
8779 line.remove_initializers(compressor, var_defs);
8780 line.definitions.forEach(process_var_def);
8787 if (stat instanceof AST_LambdaDefinition) {
8791 if (stat instanceof AST_EmptyStatement) return [];
8792 if (stat instanceof AST_SimpleStatement) return [ stat.body ];
8793 if (stat instanceof AST_Var) {
8795 stat.remove_initializers(compressor, var_defs);
8796 stat.definitions.forEach(process_var_def);
8800 function process_var_def(var_def) {
8801 if (!var_def.value) return;
8802 exprs.push(make_node(AST_Assign, var_def, {
8804 left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) {
8807 right: var_def.value
8813 OPT(AST_Switch, function(self, compressor) {
8814 if (!compressor.option("switches")) return self;
8815 if (!compressor.option("dead_code")) return self;
8821 var side_effects = [];
8822 for (var i = 0, len = self.body.length; i < len; i++) {
8823 branch = self.body[i];
8824 if (branch instanceof AST_Default) {
8825 var prev = body[body.length - 1];
8826 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
8827 eliminate_branch(branch, prev);
8830 default_branch = branch;
8833 var exp = branch.expression;
8834 var equals = make_node(AST_Binary, self, {
8836 left: self.expression,
8838 }).evaluate(compressor, true);
8840 if (exp.has_side_effects(compressor)) side_effects.push(exp);
8841 eliminate_branch(branch, body[body.length - 1]);
8844 if (!(equals instanceof AST_Node)) {
8845 if (default_branch) {
8846 var default_index = body.indexOf(default_branch);
8847 body.splice(default_index, 1);
8848 eliminate_branch(default_branch, body[default_index - 1]);
8849 default_branch = null;
8851 if (exp.has_side_effects(compressor)) {
8852 exact_match = branch;
8854 default_branch = branch = make_node(AST_Default, branch, branch);
8856 while (++i < len) eliminate_branch(self.body[i], branch);
8859 if (i + 1 >= len || aborts(branch)) {
8860 var prev = body[body.length - 1];
8861 var statements = branch.body;
8862 if (aborts(prev)) switch (prev.body.length - statements.length) {
8864 var stat = prev.body[prev.body.length - 1];
8865 if (!is_break(stat, compressor)) break;
8866 statements = statements.concat(stat);
8868 var prev_block = make_node(AST_BlockStatement, prev, prev);
8869 var next_block = make_node(AST_BlockStatement, branch, { body: statements });
8870 if (prev_block.equivalent_to(next_block)) prev.body = [];
8873 if (side_effects.length) {
8874 if (branch instanceof AST_Default) {
8875 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8877 side_effects.push(branch.expression);
8878 branch.expression = make_sequence(self, side_effects);
8884 if (side_effects.length && !exact_match) {
8885 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8887 while (branch = body[body.length - 1]) {
8888 var stat = branch.body[branch.body.length - 1];
8889 if (is_break(stat, compressor)) branch.body.pop();
8890 if (branch === default_branch) {
8891 if (!has_declarations_only(branch)) break;
8892 } else if (branch.expression.has_side_effects(compressor)) {
8894 } else if (default_branch) {
8895 if (!has_declarations_only(default_branch)) break;
8896 if (body[body.length - 2] !== default_branch) break;
8897 default_branch.body = default_branch.body.concat(branch.body);
8899 } else if (!has_declarations_only(branch)) break;
8900 eliminate_branch(branch);
8901 if (body.pop() === default_branch) default_branch = null;
8904 decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
8905 if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, {
8906 body: make_sequence(self, side_effects),
8908 return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor);
8910 if (branch === default_branch) while (branch = body[body.length - 2]) {
8911 if (branch instanceof AST_Default) break;
8912 if (!has_declarations_only(branch)) break;
8913 var exp = branch.expression;
8914 if (exp.has_side_effects(compressor)) {
8915 var prev = body[body.length - 3];
8916 if (prev && !aborts(prev)) break;
8917 default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp }));
8919 eliminate_branch(branch);
8922 body[0].body = decl.concat(body[0].body);
8924 if (compressor.option("conditionals")) switch (body.length) {
8926 if (!no_break(body[0])) break;
8927 var exp = body[0].expression;
8928 var statements = body[0].body.slice();
8929 if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
8930 condition: make_node(AST_Binary, self, {
8932 left: self.expression,
8935 body: make_node(AST_BlockStatement, self, {
8939 }).optimize(compressor);
8940 if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
8943 statements.unshift(make_node(AST_SimpleStatement, self.expression, {
8944 body:self.expression,
8946 return make_node(AST_BlockStatement, self, {
8948 }).optimize(compressor);
8950 if (!member(default_branch, body) || !no_break(body[1])) break;
8951 var statements = body[0].body.slice();
8952 var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
8953 if (exclusive) statements.pop();
8954 if (!all(statements, no_break)) break;
8955 var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1], body[1]);
8956 var node = make_node(AST_If, self, {
8957 condition: make_node(AST_Binary, self, body[0] === default_branch ? {
8959 left: self.expression,
8960 right: body[1].expression,
8963 left: self.expression,
8964 right: body[0].expression,
8966 body: make_node(AST_BlockStatement, body[0], {
8969 alternative: exclusive && alternative || null,
8971 if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, {
8972 body: [ node, alternative ],
8974 return node.optimize(compressor);
8978 function is_break(node, tw) {
8979 return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
8982 function no_break(node) {
8984 var tw = new TreeWalker(function(node) {
8986 || node instanceof AST_Lambda
8987 || node instanceof AST_SimpleStatement) return true;
8988 if (is_break(node, tw)) found = true;
8995 function eliminate_branch(branch, prev) {
8996 if (prev && !aborts(prev)) {
8997 prev.body = prev.body.concat(branch.body);
8999 extract_declarations_from_unreachable_code(compressor, branch, decl);
9004 OPT(AST_Try, function(self, compressor) {
9005 self.body = tighten_body(self.body, compressor);
9006 if (compressor.option("dead_code")) {
9007 if (has_declarations_only(self)
9008 && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
9009 return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
9013 extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
9014 body.forEach(function(stat) {
9015 if (!(stat instanceof AST_Var)) return;
9016 stat.definitions.forEach(function(var_def) {
9017 var def = var_def.name.definition().redefined();
9019 var_def.name = var_def.name.clone();
9020 var_def.name.thedef = def;
9024 body.unshift(make_node(AST_BlockStatement, self, self).optimize(compressor));
9025 if (self.bfinally) {
9026 body.push(make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor));
9028 return make_node(AST_BlockStatement, self, {
9030 }).optimize(compressor);
9032 if (self.bfinally && has_declarations_only(self.bfinally)) {
9033 var body = make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor);
9034 body = self.body.concat(body);
9035 if (!self.bcatch) return make_node(AST_BlockStatement, self, {
9037 }).optimize(compressor);
9039 self.bfinally = null;
9045 function remove_initializers(make_value) {
9046 return function(compressor, defns) {
9047 var dropped = false;
9048 this.definitions.forEach(function(defn) {
9049 if (defn.value) dropped = true;
9050 defn.name.match_symbol(function(node) {
9051 if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, {
9053 value: make_value(compressor, node)
9061 AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) {
9062 return make_node(AST_Undefined, node).optimize(compressor);
9064 AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9065 AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9067 AST_Definitions.DEFMETHOD("to_assignments", function() {
9068 var assignments = this.definitions.reduce(function(a, defn) {
9069 var def = defn.name.definition();
9070 var value = defn.value;
9072 if (value instanceof AST_Sequence) value = value.clone();
9073 var name = make_node(AST_SymbolRef, defn.name, defn.name);
9074 var assign = make_node(AST_Assign, defn, {
9080 var fixed = function() {
9081 return assign.right;
9083 fixed.assigns = [ assign ];
9084 fixed.direct_access = def.direct_access;
9085 fixed.escaped = def.escaped;
9087 def.references.forEach(function(ref) {
9088 var assigns = ref.fixed && ref.fixed.assigns;
9089 if (assigns && assigns[0] === defn) assigns[0] = assign;
9091 def.references.push(name);
9094 def.single_use = false;
9097 if (assignments.length == 0) return null;
9098 return make_sequence(this, assignments);
9101 function is_safe_lexical(def) {
9102 return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
9105 function may_overlap(compressor, def) {
9106 if (compressor.exposed(def)) return true;
9107 var scope = def.scope.resolve();
9108 for (var s = def.scope; s !== scope;) {
9110 if (s.var_names()[def.name]) return true;
9114 function to_var(stat) {
9115 return make_node(AST_Var, stat, {
9116 definitions: stat.definitions.map(function(defn) {
9117 return make_node(AST_VarDef, defn, {
9118 name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
9119 var def = name.definition();
9120 def.orig[def.orig.indexOf(node)] = name;
9121 var scope = def.scope.resolve();
9122 if (def.scope === scope) return;
9124 scope.variables.set(def.name, def);
9125 scope.enclosed.push(def);
9126 scope.var_names()[def.name] = true;
9134 function can_varify(compressor, sym) {
9135 if (!sym.fixed_value()) return false;
9136 var def = sym.definition();
9137 return is_safe_lexical(def) && same_scope(def) && !may_overlap(compressor, def);
9140 function varify(self, compressor) {
9141 return compressor.option("varify") && all(self.definitions, function(defn) {
9142 return !defn.name.match_symbol(function(node) {
9143 if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
9145 }) ? to_var(self) : self;
9148 OPT(AST_Const, varify);
9149 OPT(AST_Let, varify);
9151 function trim_optional_chain(node, compressor) {
9152 if (!compressor.option("optional_chains")) return;
9153 if (node.terminal) do {
9154 var expr = node.expression;
9155 if (node.optional) {
9156 var ev = fuzzy_eval(compressor, expr, true);
9157 if (ev == null) return make_node(AST_UnaryPrefix, node, {
9160 }).optimize(compressor);
9161 if (!(ev instanceof AST_Node)) node.optional = false;
9164 } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal);
9167 function lift_sequence_in_expression(node, compressor) {
9168 var exp = node.expression;
9169 if (!(exp instanceof AST_Sequence)) return node;
9170 var x = exp.expressions.slice();
9171 var e = node.clone();
9172 e.expression = x.pop();
9174 return make_sequence(node, x);
9177 function drop_unused_call_args(call, compressor, fns_with_marked_args) {
9178 var exp = call.expression;
9179 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9180 if (!(fn instanceof AST_Lambda)) return;
9181 if (fn.uses_arguments) return;
9182 if (fn.pinned()) return;
9183 if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
9184 var args = call.args;
9185 if (!all(args, function(arg) {
9186 return !(arg instanceof AST_Spread);
9188 var argnames = fn.argnames;
9189 var is_iife = fn === exp && !fn.name;
9191 if (!(is_iife && compressor.option("rests"))) return;
9192 var insert = argnames.length;
9193 args = args.slice(0, insert);
9194 while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor));
9195 args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) }));
9196 argnames = argnames.concat(fn.rest);
9199 args = args.slice();
9200 argnames = argnames.slice();
9202 var pos = 0, last = 0;
9203 var drop_defaults = is_iife && compressor.option("default_values");
9204 var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
9205 if (!argname) return true;
9206 if (argname instanceof AST_DestructuredArray) {
9207 return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
9209 if (argname instanceof AST_DestructuredObject) {
9210 return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
9212 return argname.__unused;
9214 var side_effects = [];
9215 for (var i = 0; i < args.length; i++) {
9216 var argname = argnames[i];
9217 if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
9218 argnames[i] = argname = argname.name;
9220 if (!argname || "__unused" in argname) {
9221 var node = args[i].drop_side_effect_free(compressor);
9222 if (drop_fargs(argname)) {
9223 if (argname) argnames.splice(i, 1);
9225 if (node) side_effects.push(node);
9229 side_effects.push(node);
9230 args[pos++] = make_sequence(call, side_effects);
9232 } else if (argname) {
9233 if (side_effects.length) {
9234 args[pos++] = make_sequence(call, side_effects);
9237 args[pos++] = make_node(AST_Number, args[i], {
9243 } else if (drop_fargs(argname, args[i])) {
9244 var node = args[i].drop_side_effect_free(compressor);
9245 argnames.splice(i, 1);
9247 if (node) side_effects.push(node);
9251 side_effects.push(args[i]);
9252 args[pos++] = make_sequence(call, side_effects);
9257 for (; i < argnames.length; i++) {
9258 if (drop_fargs(argnames[i])) argnames.splice(i--, 1);
9260 fn.argnames = argnames;
9263 if (!side_effects.length) return;
9264 var arg = make_sequence(call, side_effects);
9265 args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, {
9271 function avoid_await_yield(parent_scope) {
9273 if (is_async(parent_scope)) avoid.push("await");
9274 if (is_generator(parent_scope)) avoid.push("yield");
9275 return avoid.length && makePredicate(avoid);
9278 OPT(AST_Call, function(self, compressor) {
9279 var exp = self.expression;
9280 var terminated = trim_optional_chain(self, compressor);
9281 if (terminated) return terminated;
9282 if (compressor.option("sequences")) {
9283 if (exp instanceof AST_PropAccess) {
9284 var seq = lift_sequence_in_expression(exp, compressor);
9286 var call = self.clone();
9287 call.expression = seq.expressions.pop();
9288 seq.expressions.push(call);
9289 return seq.optimize(compressor);
9291 } else if (!needs_unbinding(compressor, exp.tail_node())) {
9292 var seq = lift_sequence_in_expression(self, compressor);
9293 if (seq !== self) return seq.optimize(compressor);
9296 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
9297 if (compressor.option("unsafe")) {
9298 if (is_undeclared_ref(exp)) switch (exp.name) {
9300 // Array(n) ---> [ , , ... , ]
9301 if (self.args.length == 1) {
9302 var first = self.args[0];
9303 if (first instanceof AST_Number) try {
9304 var length = first.value;
9305 if (length > 6) break;
9306 var elements = Array(length);
9307 for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
9308 return make_node(AST_Array, self, { elements: elements });
9310 AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
9312 file: self.start.file,
9313 line: self.start.line,
9314 col: self.start.col,
9318 if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
9320 // Array(...) ---> [ ... ]
9321 return make_node(AST_Array, self, { elements: self.args });
9324 if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
9328 if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
9329 // String(x) ---> "" + x
9330 if (self.args.length == 1) return make_node(AST_Binary, self, {
9332 left: make_node(AST_String, self, { value: "" }),
9333 right: self.args[0],
9334 }).optimize(compressor);
9338 if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
9339 // Number(x) ---> +("" + x)
9340 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9342 expression: make_node(AST_Binary, self, {
9344 left: make_node(AST_String, self, { value: "" }),
9345 right: self.args[0],
9347 }).optimize(compressor);
9350 // Boolean() ---> false
9351 if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
9352 // Boolean(x) ---> !!x
9353 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9355 expression: make_node(AST_UnaryPrefix, self, {
9357 expression: self.args[0],
9359 }).optimize(compressor);
9362 // attempt to convert RegExp(...) to literal
9364 if (all(self.args, function(arg) {
9365 var value = arg.evaluate(compressor);
9366 params.unshift(value);
9367 return arg !== value;
9369 return best_of(compressor, self, make_node(AST_RegExp, self, {
9370 value: RegExp.apply(RegExp, params),
9373 AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
9375 file: self.start.file,
9376 line: self.start.line,
9377 col: self.start.col,
9381 } else if (exp instanceof AST_Dot) switch (exp.property) {
9383 // x.toString() ---> "" + x
9384 var expr = exp.expression;
9385 if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
9386 return make_node(AST_Binary, self, {
9388 left: make_node(AST_String, self, { value: "" }),
9390 }).optimize(compressor);
9394 if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
9395 var separator = self.args[0];
9396 // [].join() ---> ""
9397 // [].join(x) ---> (x, "")
9398 if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
9399 return separator ? make_sequence(self, [
9401 make_node(AST_String, self, { value: "" }),
9402 ]).optimize(compressor) : make_node(AST_String, self, { value: "" });
9405 separator = separator.evaluate(compressor);
9406 if (separator instanceof AST_Node) break EXIT; // not a constant
9410 for (var i = 0; i < exp.expression.elements.length; i++) {
9411 var el = exp.expression.elements[i];
9412 var value = el.evaluate(compressor);
9415 } else if (el instanceof AST_Spread) {
9418 if (consts.length > 0) {
9419 elements.push(make_node(AST_String, self, {
9420 value: consts.join(separator),
9427 if (consts.length > 0) elements.push(make_node(AST_String, self, {
9428 value: consts.join(separator),
9430 // [ x ].join() ---> "" + x
9431 // [ x ].join(".") ---> "" + x
9432 // [ 1, 2, 3 ].join() ---> "1,2,3"
9433 // [ 1, 2, 3 ].join(".") ---> "1.2.3"
9434 if (elements.length == 1) {
9435 if (elements[0].is_string(compressor)) return elements[0];
9436 return make_node(AST_Binary, elements[0], {
9438 left: make_node(AST_String, self, { value: "" }),
9442 // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
9443 if (separator == "") {
9445 if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
9446 first = elements.shift();
9448 first = make_node(AST_String, self, { value: "" });
9450 return elements.reduce(function(prev, el) {
9451 return make_node(AST_Binary, el, {
9456 }, first).optimize(compressor);
9458 // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
9459 // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
9460 // need this awkward cloning to not affect original element
9461 // best_of will decide which one to get through.
9462 var node = self.clone();
9463 node.expression = node.expression.clone();
9464 node.expression.expression = node.expression.expression.clone();
9465 node.expression.expression.elements = elements;
9466 return best_of(compressor, self, node);
9470 if (self.args.length < 2) {
9471 var node = make_node(AST_Binary, self, {
9473 left: make_node(AST_Sub, self, {
9474 expression: exp.expression,
9475 property: self.args.length ? make_node(AST_Binary, self.args[0], {
9477 left: make_node(AST_Number, self, { value: 0 }),
9478 right: self.args[0],
9479 }) : make_node(AST_Number, self, { value: 0 }),
9480 }).optimize(compressor),
9481 right: make_node(AST_String, self, { value: "" }),
9483 node.is_string = return_true;
9484 return node.optimize(compressor);
9488 if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
9489 var args = self.args[1].elements.slice();
9490 args.unshift(self.args[0]);
9491 return make_node(AST_Call, self, {
9492 expression: make_node(AST_Dot, exp, {
9493 expression: exp.expression,
9497 }).optimize(compressor);
9501 var func = exp.expression;
9502 if (func instanceof AST_SymbolRef) {
9503 func = func.fixed_value();
9505 if (func instanceof AST_Lambda && !func.contains_this()) {
9506 return (self.args.length ? make_sequence(this, [
9508 make_node(AST_Call, self, {
9509 expression: exp.expression,
9510 args: self.args.slice(1)
9512 ]) : make_node(AST_Call, self, {
9513 expression: exp.expression,
9515 })).optimize(compressor);
9520 if (compressor.option("unsafe_Function")
9521 && is_undeclared_ref(exp)
9522 && exp.name == "Function") {
9523 // new Function() ---> function(){}
9524 if (self.args.length == 0) return make_node(AST_Function, self, {
9527 }).init_vars(exp.scope);
9528 if (all(self.args, function(x) {
9529 return x instanceof AST_String;
9531 // quite a corner-case, but we can handle it:
9532 // https://github.com/mishoo/UglifyJS/issues/203
9533 // if the code argument is a constant, then we can minify it.
9535 var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
9537 }).join() + "){" + self.args[self.args.length - 1].value + "})";
9538 var ast = parse(code);
9539 var mangle = { ie: compressor.option("ie") };
9540 ast.figure_out_scope(mangle);
9541 var comp = new Compressor(compressor.options);
9542 ast = ast.transform(comp);
9543 ast.figure_out_scope(mangle);
9544 ast.compute_char_frequency(mangle);
9545 ast.mangle_names(mangle);
9547 ast.walk(new TreeWalker(function(node) {
9548 if (fun) return true;
9549 if (node instanceof AST_Lambda) {
9554 var code = OutputStream();
9555 AST_BlockStatement.prototype._codegen.call(fun, code);
9557 make_node(AST_String, self, {
9558 value: fun.argnames.map(function(arg) {
9559 return arg.print_to_string();
9562 make_node(AST_String, self.args[self.args.length - 1], {
9563 value: code.get().replace(/^\{|\}$/g, "")
9568 if (ex instanceof JS_Parse_Error) {
9569 AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
9570 AST_Node.warn(ex.toString());
9577 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9578 var parent = compressor.parent(), current = compressor.self();
9579 var is_func = fn instanceof AST_Lambda
9580 && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
9581 && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
9582 var stat = is_func && fn.first_statement();
9583 var has_default = 0, has_destructured = false;
9584 var has_spread = !all(self.args, function(arg) {
9585 return !(arg instanceof AST_Spread);
9587 var can_drop = is_func && all(fn.argnames, function(argname, index) {
9588 if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
9589 if (argname instanceof AST_DefaultValue) {
9590 if (!has_default) has_default = 1;
9591 var arg = has_default == 1 && self.args[index];
9592 if (arg && !is_undefined(arg)) has_default = 2;
9593 if (has_arg_refs(argname.value)) return false;
9594 argname = argname.name;
9596 if (argname instanceof AST_Destructured) {
9597 has_destructured = true;
9598 if (has_arg_refs(argname)) return false;
9601 }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn.rest));
9602 var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
9603 if (can_inline && stat instanceof AST_Return) {
9604 var value = stat.value;
9607 && (!value || value.is_constant_expression())
9608 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9609 return make_sequence(self, convert_args(value)).optimize(compressor);
9613 var def, value, var_assigned = false;
9615 && !fn.uses_arguments
9617 && !(fn.name && fn instanceof AST_LambdaExpression)
9618 && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
9619 && fn.is_constant_expression(find_scope(compressor)))
9621 && (value = can_flatten_body(stat))
9622 && !fn.contains_this()) {
9623 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
9624 if (can_substitute_directly()) {
9625 var args = self.args.slice();
9627 var retValue = value.clone(true).transform(new TreeTransformer(function(node) {
9628 if (node instanceof AST_SymbolRef) {
9629 var def = node.definition();
9630 if (fn.variables.get(node.name) !== def) {
9634 var index = resolve_index(def);
9635 var arg = args[index];
9636 if (!arg) return make_node(AST_Undefined, self);
9638 var parent = this.parent();
9639 return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
9642 var save_inlined = fn.inlined;
9643 if (exp !== fn) fn.inlined = true;
9645 args.forEach(function(arg) {
9647 arg = arg.clone(true);
9648 arg.walk(new TreeWalker(function(node) {
9649 if (node instanceof AST_SymbolRef) refs.push(node);
9653 exprs.push(retValue);
9654 var node = make_sequence(self, exprs).optimize(compressor);
9655 fn.inlined = save_inlined;
9656 node = maintain_this_binding(compressor, parent, current, node);
9657 if (replacing || best_of_expression(node, self) === node) {
9658 refs.forEach(function(ref) {
9659 ref.scope = exp === fn ? fn.parent_scope : exp.scope;
9661 var def = ref.definition();
9662 if (replacing) def.replaced++;
9663 def.single_use = false;
9666 } else if (!node.has_side_effects(compressor)) {
9667 self.drop_side_effect_free = function(compressor, first_in_statement) {
9669 var exprs = self.args.slice();
9670 exprs.unshift(self.expression);
9671 return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement);
9675 var arg_used, insert, in_loop, scope;
9676 if (replacing && can_inject_symbols()) {
9677 fn._squeezed = true;
9678 if (exp !== fn) fn.parent_scope = exp.scope;
9679 var node = make_sequence(self, flatten_fn()).optimize(compressor);
9680 return maintain_this_binding(compressor, parent, current, node);
9683 if (compressor.option("side_effects")
9685 && all(fn.body, is_empty)
9686 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
9687 && !(is_arrow(fn) && fn.value)
9688 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9689 return make_sequence(self, convert_args()).optimize(compressor);
9692 if (compressor.option("drop_console")) {
9693 if (exp instanceof AST_PropAccess) {
9694 var name = exp.expression;
9695 while (name.expression) {
9696 name = name.expression;
9698 if (is_undeclared_ref(name) && name.name == "console") {
9699 return make_node(AST_Undefined, self).optimize(compressor);
9703 if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
9704 return self.negate(compressor, true);
9706 return try_evaluate(compressor, self);
9708 function has_arg_refs(node) {
9710 node.walk(new TreeWalker(function(node) {
9711 if (found) return true;
9712 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
9713 return found = true;
9719 function make_void_lhs(orig) {
9720 return make_node(AST_Dot, orig, {
9721 expression: make_node(AST_Array, orig, { elements: [] }),
9726 function convert_args(value) {
9727 var args = self.args.slice();
9728 var destructured = has_default > 1 || has_destructured || fn.rest;
9729 if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
9731 var tt = new TreeTransformer(function(node, descend) {
9732 if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
9733 name: node.name.transform(tt) || make_void_lhs(node),
9736 if (node instanceof AST_DestructuredArray) {
9738 node.elements.forEach(function(node, index) {
9739 node = node.transform(tt);
9740 if (node) elements[index] = node;
9742 fill_holes(node, elements);
9743 return make_node(AST_DestructuredArray, node, { elements: elements });
9745 if (node instanceof AST_DestructuredObject) {
9746 var properties = [], side_effects = [];
9747 node.properties.forEach(function(prop) {
9749 var value = prop.value.transform(tt);
9751 if (side_effects.length) {
9752 if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop);
9753 side_effects.push(key);
9754 key = make_sequence(node, side_effects);
9757 properties.push(make_node(AST_DestructuredKeyVal, prop, {
9761 } else if (key instanceof AST_Node) {
9762 side_effects.push(key);
9765 if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
9766 key: make_sequence(node, side_effects),
9767 value: make_void_lhs(node),
9769 return make_node(AST_DestructuredObject, node, { properties: properties });
9771 if (node instanceof AST_SymbolFunarg) return null;
9774 fn.argnames.forEach(function(argname, index) {
9775 argname = argname.transform(tt);
9776 if (argname) lhs[index] = argname;
9778 var rest = fn.rest && fn.rest.transform(tt);
9779 if (rest) lhs.length = fn.argnames.length;
9780 fill_holes(fn, lhs);
9781 args[0] = make_node(AST_Assign, self, {
9783 left: make_node(AST_DestructuredArray, fn, {
9789 } else fn.argnames.forEach(function(argname) {
9790 if (argname instanceof AST_DefaultValue) args.push(argname.value);
9792 args.push(value || make_node(AST_Undefined, self));
9796 function safe_from_await_yield(node, scope) {
9797 var avoid = avoid_await_yield(scope);
9798 if (!avoid) return true;
9800 var tw = new TreeWalker(function(node) {
9801 if (!safe) return true;
9802 if (node instanceof AST_Scope) {
9803 if (node === fn) return;
9804 if (is_arrow(node)) {
9805 for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
9806 } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
9811 if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
9817 function noop_value() {
9818 return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
9821 function return_value(stat) {
9822 if (!stat) return noop_value();
9823 if (stat instanceof AST_Return) return stat.value || noop_value();
9824 if (stat instanceof AST_SimpleStatement) {
9825 return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, {
9827 expression: stat.body,
9832 function can_flatten_body(stat) {
9833 var len = fn.body.length;
9835 stat = return_value(stat);
9836 if (stat) return stat;
9838 if (compressor.option("inline") < 3) return false;
9840 for (var i = 0; i < len; i++) {
9841 var line = fn.body[i];
9842 if (line instanceof AST_Var) {
9843 var assigned = var_assigned || !declarations_only(line);
9845 var_assigned = true;
9846 if (stat) return false;
9848 } else if (line instanceof AST_AsyncDefun
9849 || line instanceof AST_Defun
9850 || line instanceof AST_EmptyStatement) {
9858 return return_value(stat);
9861 function resolve_index(def) {
9862 for (var i = fn.argnames.length; --i >= 0;) {
9863 if (fn.argnames[i].definition() === def) return i;
9867 function can_substitute_directly() {
9868 if (has_default || has_destructured || var_assigned || fn.rest) return;
9869 if (compressor.option("inline") < 2 && fn.argnames.length) return;
9870 if (!fn.variables.all(function(def) {
9871 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
9873 var scope = compressor.find_parent(AST_Scope);
9875 var avoid = avoid_await_yield(scope);
9878 var side_effects = false;
9879 value.walk(new TreeWalker(function(node, descend) {
9880 if (abort) return true;
9881 if (node instanceof AST_Binary && lazy_op[node.operator]
9882 || node instanceof AST_Conditional) {
9886 if (node instanceof AST_Scope) return abort = true;
9887 if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
9888 if (node instanceof AST_SymbolRef) {
9889 var def = node.definition();
9890 if (fn.variables.get(node.name) !== def) {
9894 if (def.init instanceof AST_LambdaDefinition) return abort = true;
9895 if (is_lhs(node, this.parent())) return abort = true;
9896 var index = resolve_index(def);
9897 if (!(begin < index)) begin = index;
9898 if (!in_order) return;
9902 in_order.push(fn.argnames[index]);
9906 if (node.has_side_effects(compressor)) {
9908 side_effects = true;
9913 var end = self.args.length;
9914 if (in_order && fn.argnames.length >= end) {
9915 end = fn.argnames.length;
9916 while (end-- > begin && fn.argnames[end] === in_order.pop());
9919 return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) {
9920 return funarg.is_constant_expression(scope);
9921 } : function(funarg) {
9922 return !funarg.has_side_effects(compressor);
9926 function var_exists(defined, name) {
9927 return defined[name] || identifier_atom[name] || scope.var_names()[name];
9930 function can_inject_args(defined, used, safe_to_inject) {
9932 fn.each_argname(function(arg) {
9934 if (arg.__unused) return;
9935 if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
9936 used[arg.name] = true;
9937 if (in_loop) in_loop.push(arg.definition());
9942 function can_inject_vars(defined, used, safe_to_inject) {
9943 for (var i = 0; i < fn.body.length; i++) {
9944 var stat = fn.body[i];
9945 if (stat instanceof AST_LambdaDefinition) {
9946 if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
9947 if (!all(stat.enclosed, function(def) {
9948 return def.scope === stat || !defined[def.name];
9950 if (in_loop) in_loop.push(stat.name.definition());
9953 if (!(stat instanceof AST_Var)) continue;
9954 if (!safe_to_inject) return false;
9955 for (var j = stat.definitions.length; --j >= 0;) {
9956 var name = stat.definitions[j].name;
9957 if (var_exists(defined, name.name)) return false;
9958 if (in_loop) in_loop.push(name.definition());
9964 function can_inject_symbols() {
9965 var defined = Object.create(null);
9966 var level = 0, child;
9969 if (scope.variables) scope.variables.each(function(def) {
9970 defined[def.name] = true;
9973 scope = compressor.parent(level++);
9974 if (scope instanceof AST_DWLoop) {
9976 } else if (scope instanceof AST_For) {
9977 if (scope.init === child) continue;
9979 } else if (scope instanceof AST_ForEnumeration) {
9980 if (scope.init === child) continue;
9981 if (scope.object === child) continue;
9983 } else if (scope instanceof AST_SymbolRef) {
9984 if (scope.fixed_value() instanceof AST_Scope) return false;
9986 } while (!(scope instanceof AST_Scope));
9987 insert = scope.body.indexOf(child) + 1;
9988 if (!insert) return false;
9989 if (!safe_from_await_yield(fn, scope)) return false;
9990 var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
9991 if (scope instanceof AST_Toplevel) {
9992 if (compressor.toplevel.vars) {
9993 defined["arguments"] = true;
9995 safe_to_inject = false;
9998 var inline = compressor.option("inline");
9999 arg_used = Object.create(defined);
10000 if (!can_inject_args(defined, arg_used, inline >= 2 && safe_to_inject)) return false;
10001 var used = Object.create(arg_used);
10002 if (!can_inject_vars(defined, used, inline >= 3 && safe_to_inject)) return false;
10003 return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
10006 function append_var(decls, expressions, name, value) {
10007 var def = name.definition();
10008 if (!scope.var_names()[name.name]) {
10009 scope.var_names()[name.name] = true;
10010 decls.push(make_node(AST_VarDef, name, {
10015 scope.variables.set(name.name, def);
10016 scope.enclosed.push(def);
10017 if (!value) return;
10018 var sym = make_node(AST_SymbolRef, name, name);
10019 def.references.push(sym);
10020 expressions.push(make_node(AST_Assign, self, {
10027 function flatten_args(decls, expressions) {
10028 var len = fn.argnames.length;
10029 for (var i = self.args.length; --i >= len;) {
10030 expressions.push(self.args[i]);
10032 var default_args = [];
10033 for (i = len; --i >= 0;) {
10034 var argname = fn.argnames[i];
10036 if (argname instanceof AST_DefaultValue) {
10037 default_args.push(argname);
10038 name = argname.name;
10042 var value = self.args[i];
10043 if (name.__unused || scope.var_names()[name.name]) {
10044 if (value) expressions.push(value);
10046 var symbol = make_node(AST_SymbolVar, name, name);
10047 name.definition().orig.push(symbol);
10048 if ("__unused" in name) {
10049 append_var(decls, expressions, symbol);
10050 if (value) expressions.push(value);
10052 if (!value && in_loop && argname === name) value = make_node(AST_Undefined, self);
10053 append_var(decls, expressions, symbol, value);
10058 expressions.reverse();
10059 for (i = default_args.length; --i >= 0;) {
10060 var node = default_args[i];
10061 if ("__unused" in node.name) {
10062 expressions.push(node.value);
10064 var sym = make_node(AST_SymbolRef, node.name, node.name);
10065 node.name.definition().references.push(sym);
10066 expressions.push(make_node(AST_Assign, node, {
10075 function flatten_destructured(decls, expressions) {
10076 expressions.push(make_node(AST_Assign, self, {
10078 left: make_node(AST_DestructuredArray, self, {
10079 elements: fn.argnames.map(function(argname) {
10080 if (argname.__unused) return make_node(AST_Hole, argname);
10081 return argname.convert_symbol(AST_SymbolRef, process);
10083 rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
10085 right: make_node(AST_Array, self, { elements: self.args.slice() }),
10088 function process(ref, name) {
10089 var def = name.definition();
10090 def.references.push(ref);
10091 var symbol = make_node(AST_SymbolVar, name, name);
10092 def.orig.push(symbol);
10093 append_var(decls, expressions, symbol);
10097 function flatten_var(name) {
10098 var redef = name.definition().redefined();
10100 name = name.clone();
10101 name.thedef = redef;
10106 function flatten_vars(decls, expressions) {
10107 var args = [ insert, 0 ];
10108 var decl_var = [], expr_var = [], expr_loop = [];
10109 for (var i = 0; i < fn.body.length; i++) {
10110 var stat = fn.body[i];
10111 if (stat instanceof AST_LambdaDefinition) {
10113 var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
10114 name.definition().orig.push(name);
10115 append_var(decls, expressions, name, to_func_expr(stat, true));
10117 var def = stat.name.definition();
10118 scope.functions.set(def.name, def);
10119 scope.variables.set(def.name, def);
10120 scope.enclosed.push(def);
10121 scope.var_names()[def.name] = true;
10126 if (!(stat instanceof AST_Var)) continue;
10127 for (var j = 0; j < stat.definitions.length; j++) {
10128 var var_def = stat.definitions[j];
10129 var name = flatten_var(var_def.name);
10130 append_var(decl_var, expr_var, name, var_def.value);
10131 if (in_loop && !HOP(arg_used, name.name)) {
10132 var def = fn.variables.get(name.name);
10133 var sym = make_node(AST_SymbolRef, name, name);
10134 def.references.push(sym);
10135 expr_loop.push(make_node(AST_Assign, var_def, {
10138 right: make_node(AST_Undefined, name),
10143 [].push.apply(decls, decl_var);
10144 [].push.apply(expressions, expr_loop);
10145 [].push.apply(expressions, expr_var);
10149 function flatten_fn() {
10151 var expressions = [];
10152 if (has_default > 1 || has_destructured || fn.rest) {
10153 flatten_destructured(decls, expressions);
10155 flatten_args(decls, expressions);
10157 var args = flatten_vars(decls, expressions);
10158 expressions.push(value);
10159 if (decls.length) args.push(make_node(AST_Var, fn, {
10162 [].splice.apply(scope.body, args);
10163 fn.enclosed.forEach(function(def) {
10164 if (scope.var_names()[def.name]) return;
10165 scope.enclosed.push(def);
10166 scope.var_names()[def.name] = true;
10168 return expressions;
10172 OPT(AST_New, function(self, compressor) {
10173 if (compressor.option("sequences")) {
10174 var seq = lift_sequence_in_expression(self, compressor);
10175 if (seq !== self) return seq.optimize(compressor);
10177 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
10178 if (compressor.option("unsafe")) {
10179 var exp = self.expression;
10180 if (is_undeclared_ref(exp)) {
10181 switch (exp.name) {
10187 return make_node(AST_Call, self, self).transform(compressor);
10194 // (a = b, x && a = c) ---> a = x ? c : b
10195 // (a = b, x || a = c) ---> a = x ? b : c
10196 function to_conditional_assignment(compressor, def, value, node) {
10197 if (!(node instanceof AST_Binary)) return;
10198 if (!(node.operator == "&&" || node.operator == "||")) return;
10199 if (!(node.right instanceof AST_Assign)) return;
10200 if (node.right.operator != "=") return;
10201 if (!(node.right.left instanceof AST_SymbolRef)) return;
10202 if (node.right.left.definition() !== def) return;
10203 if (value.has_side_effects(compressor)) return;
10204 if (!safe_from_assignment(node.left)) return;
10205 if (!safe_from_assignment(node.right.right)) return;
10207 return node.operator == "&&" ? make_node(AST_Conditional, node, {
10208 condition: node.left,
10209 consequent: node.right.right,
10211 }) : make_node(AST_Conditional, node, {
10212 condition: node.left,
10214 alternative: node.right.right
10217 function safe_from_assignment(node) {
10218 if (node.has_side_effects(compressor)) return;
10220 node.walk(new TreeWalker(function(node) {
10221 if (hit) return true;
10222 if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
10228 OPT(AST_Sequence, function(self, compressor) {
10229 var expressions = filter_for_side_effects();
10230 var end = expressions.length - 1;
10231 merge_assignments();
10232 trim_right_for_undefined();
10234 self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
10235 if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
10238 self.expressions = expressions;
10241 function filter_for_side_effects() {
10242 if (!compressor.option("side_effects")) return self.expressions;
10243 var expressions = [];
10244 var first = first_in_statement(compressor);
10245 var last = self.expressions.length - 1;
10246 self.expressions.forEach(function(expr, index) {
10247 if (index < last) expr = expr.drop_side_effect_free(compressor, first);
10249 merge_sequence(expressions, expr);
10253 return expressions;
10256 function trim_right_for_undefined() {
10257 if (!compressor.option("side_effects")) return;
10258 while (end > 0 && is_undefined(expressions[end], compressor)) end--;
10259 if (end < expressions.length - 1) {
10260 expressions[end] = make_node(AST_UnaryPrefix, self, {
10262 expression : expressions[end]
10264 expressions.length = end + 1;
10268 function is_simple_assign(node) {
10269 return node instanceof AST_Assign
10270 && node.operator == "="
10271 && node.left instanceof AST_SymbolRef
10272 && node.left.definition();
10275 function merge_assignments() {
10276 for (var i = 1; i < end; i++) {
10277 var prev = expressions[i - 1];
10278 var def = is_simple_assign(prev);
10279 if (!def) continue;
10280 var expr = expressions[i];
10281 if (compressor.option("conditionals")) {
10282 var cond = to_conditional_assignment(compressor, def, prev.right, expr);
10285 expressions.splice(i--, 1);
10290 if (compressor.option("dead_code")
10291 && is_simple_assign(expr) === def
10292 && expr.right.is_constant_expression(def.scope.resolve())) {
10293 expressions[--i] = prev.right;
10299 OPT(AST_UnaryPostfix, function(self, compressor) {
10300 if (compressor.option("sequences")) {
10301 var seq = lift_sequence_in_expression(self, compressor);
10302 if (seq !== self) return seq.optimize(compressor);
10304 return try_evaluate(compressor, self);
10307 var SIGN_OPS = makePredicate("+ -");
10308 var MULTIPLICATIVE_OPS = makePredicate("* / %");
10309 OPT(AST_UnaryPrefix, function(self, compressor) {
10310 var op = self.operator;
10311 var exp = self.expression;
10312 if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
10313 return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
10315 if (compressor.option("sequences") && can_lift()) {
10316 var seq = lift_sequence_in_expression(self, compressor);
10317 if (seq !== self) return seq.optimize(compressor);
10319 if (compressor.option("side_effects") && op == "void") {
10320 exp = exp.drop_side_effect_free(compressor);
10321 if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
10322 self.expression = exp;
10325 if (compressor.option("booleans")) {
10326 if (op == "!" && exp.is_truthy()) {
10327 return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
10328 } else if (compressor.in_boolean_context()) switch (op) {
10330 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
10331 // !!foo ---> foo, if we're in boolean context
10332 return exp.expression;
10334 if (exp instanceof AST_Binary) {
10335 self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
10339 // typeof always returns a non-empty string, thus it's
10340 // always true in booleans
10341 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
10342 var exprs = [ make_node(AST_True, self) ];
10343 if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
10344 return make_sequence(self, exprs).optimize(compressor);
10347 if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
10348 if (compressor.option("evaluate")
10349 && exp instanceof AST_Binary
10351 && MULTIPLICATIVE_OPS[exp.operator]
10352 && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
10353 return make_node(AST_Binary, self, {
10354 operator: exp.operator,
10355 left: make_node(AST_UnaryPrefix, exp.left, {
10357 expression: exp.left
10362 // avoids infinite recursion of numerals
10363 return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
10364 ? self : try_evaluate(compressor, self);
10366 function may_not_delete(node) {
10367 return node instanceof AST_Infinity
10368 || node instanceof AST_NaN
10369 || node instanceof AST_NewTarget
10370 || node instanceof AST_PropAccess
10371 || node instanceof AST_SymbolRef
10372 || node instanceof AST_Undefined;
10375 function can_lift() {
10378 return !may_not_delete(exp.tail_node());
10380 return !is_undeclared_ref(exp.tail_node());
10387 OPT(AST_Await, function(self, compressor) {
10388 if (!compressor.option("awaits")) return self;
10389 if (compressor.option("sequences")) {
10390 var seq = lift_sequence_in_expression(self, compressor);
10391 if (seq !== self) return seq.optimize(compressor);
10393 if (compressor.option("side_effects")) {
10394 var exp = self.expression;
10395 if (exp instanceof AST_Await) return exp.optimize(compressor);
10396 if (exp instanceof AST_UnaryPrefix) {
10397 if (exp.expression instanceof AST_Await) return exp.optimize(compressor);
10398 if (exp.operator == "void") return make_node(AST_UnaryPrefix, self, {
10400 expression: make_node(AST_Await, self, { expression: exp.expression }),
10401 }).optimize(compressor);
10403 for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) {
10404 if (is_arrow(parent)) {
10405 if (parent.value === node) return exp.optimize(compressor);
10406 } else if (parent instanceof AST_Return) {
10410 parent = compressor.parent(level++);
10411 if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) {
10415 } while (parent && !(parent instanceof AST_Scope));
10416 if (drop) return exp.optimize(compressor);
10417 } else if (parent instanceof AST_Sequence) {
10418 if (parent.tail_node() === node) continue;
10426 OPT(AST_Yield, function(self, compressor) {
10427 if (!compressor.option("yields")) return self;
10428 if (compressor.option("sequences")) {
10429 var seq = lift_sequence_in_expression(self, compressor);
10430 if (seq !== self) return seq.optimize(compressor);
10432 var exp = self.expression;
10433 if (self.nested && exp.TYPE == "Call") {
10434 var inlined = exp.clone().optimize(compressor);
10435 if (inlined.TYPE != "Call") return inlined;
10440 AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
10441 if (this.left instanceof AST_PropAccess) {
10442 if (!(this.left.expression instanceof AST_Sequence)) return this;
10443 var x = this.left.expression.expressions.slice();
10444 var e = this.clone();
10445 e.left = e.left.clone();
10446 e.left.expression = x.pop();
10448 return make_sequence(this, x);
10450 if (this.left instanceof AST_Sequence) {
10451 var x = this.left.expressions.slice();
10452 var e = this.clone();
10455 return make_sequence(this, x);
10457 if (this.right instanceof AST_Sequence) {
10458 if (this.left.has_side_effects(compressor)) return this;
10459 var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
10460 var x = this.right.expressions;
10461 var last = x.length - 1;
10462 for (var i = 0; i < last; i++) {
10463 if (!assign && x[i].has_side_effects(compressor)) break;
10467 var e = this.clone();
10470 return make_sequence(this, x);
10473 var e = this.clone();
10474 e.right = make_sequence(this.right, x.slice(i));
10477 return make_sequence(this, x);
10483 var indexFns = makePredicate("indexOf lastIndexOf");
10484 var commutativeOperators = makePredicate("== === != !== * & | ^");
10485 function is_object(node) {
10486 if (node instanceof AST_Assign) return node.operator == "=" && is_object(node.right);
10487 if (node instanceof AST_Sequence) return is_object(node.tail_node());
10488 if (node instanceof AST_SymbolRef) return is_object(node.fixed_value());
10489 return node instanceof AST_Array
10490 || node instanceof AST_Class
10491 || node instanceof AST_Lambda
10492 || node instanceof AST_New
10493 || node instanceof AST_Object;
10496 function is_primitive(compressor, node) {
10497 if (node.is_constant()) return true;
10498 if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right);
10499 if (node instanceof AST_Binary) {
10500 return !lazy_op[node.operator]
10501 || is_primitive(compressor, node.left) && is_primitive(compressor, node.right);
10503 if (node instanceof AST_Conditional) {
10504 return is_primitive(compressor, node.consequent) && is_primitive(compressor, node.alternative);
10506 if (node instanceof AST_Sequence) return is_primitive(compressor, node.tail_node());
10507 if (node instanceof AST_SymbolRef) {
10508 var fixed = node.fixed_value();
10509 return fixed && is_primitive(compressor, fixed);
10511 if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag);
10512 if (node instanceof AST_Unary) return true;
10515 function repeatable(compressor, node) {
10516 if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
10517 if (node instanceof AST_Sub) {
10518 return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
10520 if (node instanceof AST_Symbol) return true;
10521 return !node.has_side_effects(compressor);
10524 OPT(AST_Binary, function(self, compressor) {
10525 function reversible() {
10526 return self.left.is_constant()
10527 || self.right.is_constant()
10528 || !self.left.has_side_effects(compressor)
10529 && !self.right.has_side_effects(compressor);
10531 function reverse(op) {
10532 if (reversible()) {
10533 if (op) self.operator = op;
10534 var tmp = self.left;
10535 self.left = self.right;
10539 function swap_chain() {
10540 var rhs = self.right;
10541 self.left = make_node(AST_Binary, self, {
10542 operator: self.operator,
10545 start: self.left.start,
10548 self.right = rhs.right;
10549 self.left = self.left.transform(compressor);
10551 if (commutativeOperators[self.operator]
10552 && self.right.is_constant()
10553 && !self.left.is_constant()
10554 && !(self.left instanceof AST_Binary
10555 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10556 // if right is a constant, whatever side effects the
10557 // left side might have could not influence the
10558 // result. hence, force switch.
10561 if (compressor.option("sequences")) {
10562 var seq = self.lift_sequences(compressor);
10563 if (seq !== self) return seq.optimize(compressor);
10565 if (compressor.option("assignments") && lazy_op[self.operator]) {
10566 var assign = self.right;
10567 // a || (a = x) ---> a = a || x
10568 // a && (a = x) ---> a = a && x
10569 if (self.left instanceof AST_SymbolRef
10570 && assign instanceof AST_Assign
10571 && assign.operator == "="
10572 && self.left.equivalent_to(assign.left)) {
10573 self.right = assign.right;
10574 assign.right = self;
10578 if (compressor.option("comparisons")) switch (self.operator) {
10581 if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
10582 AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
10583 return make_sequence(self, [
10585 make_node(self.operator == "===" ? AST_False : AST_True, self)
10586 ]).optimize(compressor);
10588 var is_strict_comparison = true;
10589 if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
10590 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
10591 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
10592 repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
10593 self.operator = self.operator.slice(0, 2);
10595 // XXX: intentionally falling down to the next case
10598 // void 0 == x ---> null == x
10599 if (!is_strict_comparison && is_undefined(self.left, compressor)) {
10600 self.left = make_node(AST_Null, self.left);
10602 // "undefined" == typeof x ---> undefined === x
10603 else if (compressor.option("typeofs")
10604 && self.left instanceof AST_String
10605 && self.left.value == "undefined"
10606 && self.right instanceof AST_UnaryPrefix
10607 && self.right.operator == "typeof") {
10608 var expr = self.right.expression;
10609 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
10610 : !(expr instanceof AST_PropAccess && compressor.option("ie"))) {
10612 self.left = make_node(AST_Undefined, self.left).optimize(compressor);
10613 if (self.operator.length == 2) self.operator += "=";
10616 // obj !== obj ---> false
10617 else if (self.left instanceof AST_SymbolRef
10618 && self.right instanceof AST_SymbolRef
10619 && self.left.definition() === self.right.definition()
10620 && is_object(self.left)) {
10621 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
10626 // void 0 !== x && null !== x ---> null != x
10627 // void 0 === x || null === x ---> null == x
10628 var lhs = self.left;
10629 if (lhs.operator == self.operator) {
10632 if (lhs instanceof AST_Binary
10633 && lhs.operator == (self.operator == "&&" ? "!==" : "===")
10634 && self.right instanceof AST_Binary
10635 && lhs.operator == self.right.operator
10636 && (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
10637 || lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
10638 && !lhs.right.has_side_effects(compressor)
10639 && lhs.right.equivalent_to(self.right.right)) {
10640 var combined = make_node(AST_Binary, self, {
10641 operator: lhs.operator.slice(0, -1),
10642 left: make_node(AST_Null, self),
10645 if (lhs !== self.left) {
10646 combined = make_node(AST_Binary, self, {
10647 operator: self.operator,
10648 left: self.left.left,
10656 var in_bool = false;
10657 var parent = compressor.parent();
10658 if (compressor.option("booleans")) {
10659 var lhs = self.left;
10660 if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
10661 if (lhs.equivalent_to(self.right)) {
10662 return maintain_this_binding(compressor, parent, compressor.self(), lhs).optimize(compressor);
10664 mark_duplicate_condition(compressor, lhs);
10666 in_bool = compressor.in_boolean_context();
10668 if (in_bool) switch (self.operator) {
10670 var ll = self.left.evaluate(compressor);
10671 var rr = self.right.evaluate(compressor);
10672 if (ll && typeof ll == "string") {
10673 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10674 return make_sequence(self, [
10676 make_node(AST_True, self)
10677 ]).optimize(compressor);
10679 if (rr && typeof rr == "string") {
10680 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10681 return make_sequence(self, [
10683 make_node(AST_True, self)
10684 ]).optimize(compressor);
10688 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10689 return make_node(AST_UnaryPrefix, self, {
10691 expression: self.right
10692 }).optimize(compressor);
10696 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10697 return self.right.optimize(compressor);
10701 if (compressor.option("comparisons") && self.is_boolean(compressor)) {
10702 if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
10703 var negated = best_of(compressor, self, make_node(AST_UnaryPrefix, self, {
10705 expression: self.negate(compressor, first_in_statement(compressor))
10707 if (negated !== self) return negated;
10709 switch (self.operator) {
10710 case ">": reverse("<"); break;
10711 case ">=": reverse("<="); break;
10714 // x && (y && z) ---> x && y && z
10715 // x || (y || z) ---> x || y || z
10716 if (compressor.option("conditionals")
10717 && lazy_op[self.operator]
10718 && self.right instanceof AST_Binary
10719 && self.operator == self.right.operator) {
10722 if (compressor.option("strings") && self.operator == "+") {
10723 // "foo" + 42 + "" ---> "foo" + 42
10724 if (self.right instanceof AST_String
10725 && self.right.value == ""
10726 && self.left.is_string(compressor)) {
10727 return self.left.optimize(compressor);
10729 // "" + ("foo" + 42) ---> "foo" + 42
10730 if (self.left instanceof AST_String
10731 && self.left.value == ""
10732 && self.right.is_string(compressor)) {
10733 return self.right.optimize(compressor);
10735 // "" + 42 + "foo" ---> 42 + "foo"
10736 if (self.left instanceof AST_Binary
10737 && self.left.operator == "+"
10738 && self.left.left instanceof AST_String
10739 && self.left.left.value == ""
10740 && self.right.is_string(compressor)
10741 && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) {
10742 self.left = self.left.right;
10743 return self.optimize(compressor);
10745 // "x" + (y + "z") ---> "x" + y + "z"
10746 // x + ("y" + z) ---> x + "y" + z
10747 if (self.right instanceof AST_Binary
10748 && self.operator == self.right.operator
10749 && (self.left.is_string(compressor) && self.right.is_string(compressor)
10750 || self.right.left.is_string(compressor)
10751 && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
10755 if (compressor.option("evaluate")) {
10756 var associative = true;
10757 switch (self.operator) {
10759 var ll = fuzzy_eval(compressor, self.left);
10761 AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
10762 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10763 } else if (!(ll instanceof AST_Node)) {
10764 AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
10765 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10767 var rr = self.right.evaluate(compressor);
10770 AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
10771 return make_sequence(self, [
10773 make_node(AST_False, self)
10774 ]).optimize(compressor);
10775 } else self.falsy = true;
10776 } else if (!(rr instanceof AST_Node)) {
10777 if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
10778 AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
10779 return self.left.optimize(compressor);
10782 // (x || false) && y ---> x ? y : false
10783 if (self.left.operator == "||") {
10784 var lr = fuzzy_eval(compressor, self.left.right);
10785 if (!lr) return make_node(AST_Conditional, self, {
10786 condition: self.left.left,
10787 consequent: self.right,
10788 alternative: self.left.right
10789 }).optimize(compressor);
10793 var nullish = true;
10795 var ll = fuzzy_eval(compressor, self.left, nullish);
10796 if (nullish ? ll == null : !ll) {
10797 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10798 operator: self.operator,
10799 value: nullish ? "nulish" : "false",
10800 file: self.start.file,
10801 line: self.start.line,
10802 col: self.start.col,
10804 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10805 } else if (!(ll instanceof AST_Node)) {
10806 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10807 operator: self.operator,
10808 value: nullish ? "defined" : "true",
10809 file: self.start.file,
10810 line: self.start.line,
10811 col: self.start.col,
10813 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10815 var rr = self.right.evaluate(compressor);
10817 if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
10818 AST_Node.warn("Dropping side-effect-free {operator} [{file}:{line},{col}]", {
10819 operator: self.operator,
10820 file: self.start.file,
10821 line: self.start.line,
10822 col: self.start.col,
10824 return self.left.optimize(compressor);
10826 } else if (!nullish && !(rr instanceof AST_Node)) {
10828 AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
10829 return make_sequence(self, [
10831 make_node(AST_True, self)
10832 ]).optimize(compressor);
10833 } else self.truthy = true;
10835 // x && true || y ---> x ? true : y
10836 if (!nullish && self.left.operator == "&&") {
10837 var lr = fuzzy_eval(compressor, self.left.right);
10838 if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
10839 condition: self.left.left,
10840 consequent: self.left.right,
10841 alternative: self.right
10842 }).optimize(compressor);
10846 // "foo" + ("bar" + x) ---> "foobar" + x
10847 if (self.left instanceof AST_Constant
10848 && self.right instanceof AST_Binary
10849 && self.right.operator == "+"
10850 && self.right.left instanceof AST_Constant
10851 && self.right.is_string(compressor)) {
10852 self = make_node(AST_Binary, self, {
10854 left: make_node(AST_String, self.left, {
10855 value: "" + self.left.value + self.right.left.value,
10856 start: self.left.start,
10857 end: self.right.left.end
10859 right: self.right.right
10862 // (x + "foo") + "bar" ---> x + "foobar"
10863 if (self.right instanceof AST_Constant
10864 && self.left instanceof AST_Binary
10865 && self.left.operator == "+"
10866 && self.left.right instanceof AST_Constant
10867 && self.left.is_string(compressor)) {
10868 self = make_node(AST_Binary, self, {
10870 left: self.left.left,
10871 right: make_node(AST_String, self.right, {
10872 value: "" + self.left.right.value + self.right.value,
10873 start: self.left.right.start,
10874 end: self.right.end
10878 // a + -b ---> a - b
10879 if (self.right instanceof AST_UnaryPrefix
10880 && self.right.operator == "-"
10881 && self.left.is_number(compressor)) {
10882 self = make_node(AST_Binary, self, {
10885 right: self.right.expression
10889 // -a + b ---> b - a
10890 if (self.left instanceof AST_UnaryPrefix
10891 && self.left.operator == "-"
10893 && self.right.is_number(compressor)) {
10894 self = make_node(AST_Binary, self, {
10897 right: self.left.expression
10901 // (a + b) + 3 ---> 3 + (a + b)
10902 if (compressor.option("unsafe_math")
10903 && self.left instanceof AST_Binary
10904 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10905 && self.right.is_constant()
10906 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10907 && self.left.is_number(compressor)
10908 && !self.left.right.is_constant()
10909 && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
10910 self = make_node(AST_Binary, self, {
10911 operator: self.left.operator,
10912 left: make_node(AST_Binary, self, {
10913 operator: self.operator,
10915 right: self.left.left
10917 right: self.left.right
10922 // a - -b ---> a + b
10923 if (self.right instanceof AST_UnaryPrefix
10924 && self.right.operator == "-"
10925 && self.left.is_number(compressor)
10926 && self.right.expression.is_number(compressor)) {
10927 self = make_node(AST_Binary, self, {
10930 right: self.right.expression
10936 associative = compressor.option("unsafe_math");
10937 // +a - b ---> a - b
10938 // a - +b ---> a - b
10939 if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
10940 var node = self[operand];
10941 if (node instanceof AST_UnaryPrefix && node.operator == "+") {
10942 var exp = node.expression;
10943 if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) {
10944 self[operand] = exp;
10951 // a + +b ---> +b + a
10952 if (self.operator != "-"
10953 && self.operator != "/"
10954 && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
10955 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10957 && !(self.left instanceof AST_Binary
10958 && self.left.operator != self.operator
10959 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10960 var reversed = make_node(AST_Binary, self, {
10961 operator: self.operator,
10965 if (self.right instanceof AST_Constant
10966 && !(self.left instanceof AST_Constant)) {
10967 self = best_of(compressor, reversed, self);
10969 self = best_of(compressor, self, reversed);
10972 if (!associative || !self.is_number(compressor)) break;
10973 // a + (b + c) ---> (a + b) + c
10974 if (self.right instanceof AST_Binary
10975 && self.right.operator != "%"
10976 && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
10977 && self.right.is_number(compressor)
10978 && (self.operator != "+"
10979 || self.right.left.is_boolean(compressor)
10980 || self.right.left.is_number(compressor))
10981 && (self.operator != "-" || !self.left.is_negative_zero())
10982 && (self.right.left.is_constant_expression()
10983 || !self.right.right.has_side_effects(compressor))
10984 && !is_modify_array(self.right.right)) {
10985 self = make_node(AST_Binary, self, {
10986 operator: align(self.operator, self.right.operator),
10987 left: make_node(AST_Binary, self.left, {
10988 operator: self.operator,
10990 right: self.right.left,
10991 start: self.left.start,
10992 end: self.right.left.end
10994 right: self.right.right
10996 if (self.operator == "+"
10997 && !self.right.is_boolean(compressor)
10998 && !self.right.is_number(compressor)) {
10999 self.right = make_node(AST_UnaryPrefix, self.right, {
11001 expression: self.right
11005 // (2 * n) * 3 ---> 6 * n
11006 // (n + 2) + 3 ---> n + 5
11007 if (self.right instanceof AST_Constant
11008 && self.left instanceof AST_Binary
11009 && self.left.operator != "%"
11010 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
11011 && self.left.is_number(compressor)) {
11012 if (self.left.left instanceof AST_Constant) {
11013 var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
11014 self = make_binary(self, self.left.operator, try_evaluate(compressor, lhs), self.left.right);
11015 } else if (self.left.right instanceof AST_Constant) {
11016 var op = align(self.left.operator, self.operator);
11017 var rhs = try_evaluate(compressor, make_binary(self.left, op, self.left.right, self.right));
11018 if (rhs.is_constant()
11019 && !(self.left.operator == "-"
11020 && self.right.value != 0
11022 && self.left.left.is_negative_zero())) {
11023 self = make_binary(self, self.left.operator, self.left.left, rhs);
11029 if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11030 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
11033 if (self.left.value == 0) {
11034 if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11036 expression: self.right
11037 }).optimize(compressor);
11038 if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
11043 if (self.left.value == 1) {
11044 return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
11046 expression: self.right
11047 }).optimize(compressor);
11051 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
11054 if (self.right.value == 0) {
11055 if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11057 expression: self.left
11058 }).optimize(compressor);
11059 if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
11064 if (self.right.value == 0) {
11065 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11067 expression: self.left
11068 }).optimize(compressor);
11073 if (self.right.value == 1) {
11074 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11076 expression: self.left
11077 }).optimize(compressor);
11083 if (compressor.option("typeofs")) switch (self.operator) {
11085 mark_locally_defined(self.left, self.right, null);
11088 mark_locally_defined(self.left, null, self.right);
11091 if (compressor.option("unsafe")) {
11092 var indexRight = is_indexFn(self.right);
11095 && (self.operator == "==" || self.operator == "!=")
11096 && self.left instanceof AST_Number
11097 && self.left.value == 0) {
11098 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
11100 expression: self.right
11101 }) : self.right).optimize(compressor);
11103 var indexLeft = is_indexFn(self.left);
11104 if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
11105 var node = make_node(AST_UnaryPrefix, self, {
11107 expression: make_node(AST_UnaryPrefix, self, {
11109 expression: indexLeft ? self.left : self.right
11112 switch (self.operator) {
11114 if (indexLeft) break;
11117 node = make_node(AST_UnaryPrefix, self, {
11123 return node.optimize(compressor);
11126 return try_evaluate(compressor, self);
11128 function is_modify_array(node) {
11130 node.walk(new TreeWalker(function(node) {
11131 if (found) return true;
11132 if (node instanceof AST_Assign) {
11133 if (node.left instanceof AST_PropAccess) return found = true;
11134 } else if (node instanceof AST_Unary) {
11135 if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) {
11136 return found = true;
11143 function align(ref, op) {
11146 return op == "+" ? "-" : "+";
11148 return op == "*" ? "/" : "*";
11154 function make_binary(orig, op, left, right, start, end) {
11156 if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
11157 left = make_node(AST_UnaryPrefix, left, {
11162 if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
11163 right = make_node(AST_UnaryPrefix, right, {
11169 return make_node(AST_Binary, orig, {
11178 function is_indexFn(node) {
11179 return node.TYPE == "Call"
11180 && node.expression instanceof AST_Dot
11181 && indexFns[node.expression.property];
11184 function is_indexOf_match_pattern() {
11185 switch (self.operator) {
11187 // 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
11188 return indexRight && self.left instanceof AST_Number && self.left.value == 0;
11190 // array.indexOf(string) < 0 ---> !~array.indexOf(string)
11191 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
11192 // -1 < array.indexOf(string) ---> !!~array.indexOf(string)
11195 // -1 == array.indexOf(string) ---> !~array.indexOf(string)
11196 // -1 != array.indexOf(string) ---> !!~array.indexOf(string)
11197 if (!indexRight) return false;
11198 return self.left instanceof AST_Number && self.left.value == -1
11199 || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
11200 && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
11205 OPT(AST_SymbolExport, function(self) {
11209 function recursive_ref(compressor, def, fn) {
11210 var level = 0, node = compressor.self();
11212 if (node === fn) return node;
11213 if (is_lambda(node) && node.name && node.name.definition() === def) return node;
11214 } while (node = compressor.parent(level++));
11217 function same_scope(def) {
11218 var scope = def.scope.resolve();
11219 return all(def.references, function(ref) {
11220 return scope === ref.scope.resolve();
11224 OPT(AST_SymbolRef, function(self, compressor) {
11225 if (!compressor.option("ie")
11226 && is_undeclared_ref(self)
11227 // testing against `self.scope.uses_with` is an optimization
11228 && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
11229 switch (self.name) {
11231 return make_node(AST_Undefined, self).optimize(compressor);
11233 return make_node(AST_NaN, self).optimize(compressor);
11235 return make_node(AST_Infinity, self).optimize(compressor);
11238 var parent = compressor.parent();
11239 if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
11240 var def = self.definition();
11241 var fixed = self.fixed_value();
11242 var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
11244 if (is_lambda(fixed)) {
11245 if ((def.scope !== self.scope.resolve() || def.in_loop)
11246 && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
11247 single_use = false;
11248 } else if (recursive_ref(compressor, def, fixed)) {
11249 single_use = false;
11250 } else if (fixed.name && fixed.name.definition() !== def) {
11251 single_use = false;
11252 } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
11253 single_use = fixed.is_constant_expression(self.scope);
11254 if (single_use == "f") {
11255 var scope = self.scope;
11257 if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
11258 scope.inlined = true;
11260 } while (scope = scope.parent_scope);
11262 } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
11263 || fixed.name.name == "yield" && is_generator(fixed))) {
11264 single_use = false;
11265 } else if (fixed.has_side_effects(compressor)) {
11266 single_use = false;
11267 } else if (compressor.option("ie") && fixed instanceof AST_Class) {
11268 single_use = false;
11270 if (single_use) fixed.parent_scope = self.scope;
11272 || def.recursive_refs > 0
11273 || !fixed.is_constant_expression()
11274 || fixed.drop_side_effect_free(compressor)) {
11275 single_use = false;
11279 def.single_use = false;
11280 fixed._squeezed = true;
11281 fixed.single_use = true;
11282 if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
11283 if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
11284 if (is_lambda(fixed)) {
11285 var scope = self.scope.resolve();
11286 fixed.enclosed.forEach(function(def) {
11287 if (fixed.variables.has(def.name)) return;
11288 if (scope.var_names()[def.name]) return;
11289 scope.enclosed.push(def);
11290 scope.var_names()[def.name] = true;
11294 if (def.recursive_refs > 0) {
11295 value = fixed.clone(true);
11296 var defun_def = value.name.definition();
11297 var lambda_def = value.variables.get(value.name.name);
11298 var name = lambda_def && lambda_def.orig[0];
11299 var def_fn_name, symbol_type;
11300 if (value instanceof AST_Class) {
11301 def_fn_name = "def_function";
11302 symbol_type = AST_SymbolClass;
11304 def_fn_name = "def_variable";
11305 symbol_type = AST_SymbolLambda;
11307 if (!(name instanceof symbol_type)) {
11308 name = make_node(symbol_type, value.name, value.name);
11309 name.scope = value;
11311 lambda_def = value[def_fn_name](name);
11312 lambda_def.recursive_refs = def.recursive_refs;
11314 value.walk(new TreeWalker(function(node) {
11315 if (node instanceof AST_SymbolDeclaration) {
11316 if (node !== name) {
11317 var def = node.definition();
11318 def.orig.push(node);
11323 if (!(node instanceof AST_SymbolRef)) return;
11324 var def = node.definition();
11325 if (def === defun_def) {
11326 node.thedef = def = lambda_def;
11328 def.single_use = false;
11329 var fn = node.fixed_value();
11332 && fn.name.definition() === def
11333 && def.scope === fn.name.scope
11334 && fixed.variables.get(fn.name.name) === def) {
11335 fn.name = fn.name.clone();
11336 node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
11339 def.references.push(node);
11342 if (fixed instanceof AST_Scope) {
11343 compressor.push(fixed);
11344 value = fixed.optimize(compressor);
11347 value = fixed.optimize(compressor);
11349 value = value.transform(new TreeTransformer(function(node, descend) {
11350 if (node instanceof AST_Scope) return node;
11351 node = node.clone();
11352 descend(node, this);
11359 var local = self.fixed !== def.fixed;
11360 if (fixed && (local || def.should_replace !== false)) {
11362 if (fixed instanceof AST_This) {
11363 if (!is_funarg(def) && same_scope(def)) init = fixed;
11364 } else if ((ev = fixed.evaluate(compressor, true)) !== fixed
11365 && typeof ev != "function"
11367 || typeof ev != "object"
11368 || compressor.option("unsafe_regexp")
11369 && ev instanceof RegExp && !def.cross_loop && same_scope(def))) {
11370 init = make_node_from_constant(ev, fixed);
11373 if (!local && def.should_replace === undefined) {
11374 var value_length = init.optimize(compressor).print_to_string().length;
11375 if (!has_symbol_ref(fixed)) {
11376 value_length = Math.min(value_length, fixed.print_to_string().length);
11378 var name_length = def.name.length;
11379 if (compressor.option("unused") && !compressor.exposed(def)) {
11380 var referenced = def.references.length - def.replaced;
11381 name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
11383 var delta = value_length - Math.floor(name_length);
11384 def.should_replace = delta < compressor.eval_threshold;
11386 if (local || def.should_replace) {
11388 if (has_symbol_ref(fixed)) {
11389 value = init.optimize(compressor);
11390 if (value === init) value = value.clone(true);
11392 value = best_of_expression(init.optimize(compressor), fixed);
11393 if (value === init || value === fixed) value = value.clone(true);
11403 function has_symbol_ref(value) {
11405 value.walk(new TreeWalker(function(node) {
11406 if (node instanceof AST_SymbolRef) found = true;
11407 if (found) return true;
11413 function is_raw_tag(compressor, tag) {
11414 return compressor.option("unsafe")
11415 && tag instanceof AST_Dot
11416 && tag.property == "raw"
11417 && is_undeclared_ref(tag.expression)
11418 && tag.expression.name == "String";
11421 function decode_template(str) {
11422 var malformed = false;
11423 str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
11424 var ch = decode_escape_sequence(seq);
11425 if (typeof ch == "string") return ch;
11428 if (!malformed) return str;
11431 OPT(AST_Template, function(self, compressor) {
11432 if (!compressor.option("templates")) return self;
11433 var tag = self.tag;
11434 if (!tag || is_raw_tag(compressor, tag)) {
11437 for (var i = 0, status; i < self.strings.length; i++) {
11438 var str = self.strings[i];
11440 var trimmed = decode_template(str);
11441 if (trimmed) str = escape_literal(trimmed);
11444 var node = self.expressions[i - 1];
11445 var value = should_join(node);
11447 var prev = strs[strs.length - 1];
11448 var joined = prev + value + str;
11450 if (tag || typeof (decoded = decode_template(joined)) == status) {
11451 strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined;
11458 if (!tag) status = typeof trimmed;
11460 if (!tag && strs.length > 1) {
11461 if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, {
11463 left: make_node(AST_Template, self, {
11464 expressions: exprs.slice(0, -1),
11465 strings: strs.slice(0, -1),
11467 }).transform(compressor),
11468 right: exprs[exprs.length - 1],
11469 }).optimize(compressor);
11470 if (strs[0] == "") {
11471 var left = make_node(AST_Binary, self, {
11473 left: make_node(AST_String, self, { value: "" }),
11476 for (var i = 1; strs[i] == "" && i < exprs.length; i++) {
11477 left = make_node(AST_Binary, self, {
11483 return best_of(compressor, self, make_node(AST_Binary, self, {
11485 left: left.transform(compressor),
11486 right: make_node(AST_Template, self, {
11487 expressions: exprs.slice(i),
11488 strings: strs.slice(i),
11490 }).transform(compressor),
11491 }).optimize(compressor));
11494 self.expressions = exprs;
11495 self.strings = strs;
11497 return try_evaluate(compressor, self);
11499 function escape_literal(str) {
11500 return str.replace(/\r|\\|`|\${/g, function(s) {
11501 return "\\" + (s == "\r" ? "r" : s);
11505 function should_join(node) {
11506 var ev = node.evaluate(compressor);
11507 if (ev === node) return;
11508 if (tag && /\r|\\|`/.test(ev)) return;
11509 ev = escape_literal("" + ev);
11510 if (ev.length > node.print_to_string().length + "${}".length) return;
11515 function is_atomic(lhs, self) {
11516 return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
11519 OPT(AST_Undefined, function(self, compressor) {
11520 if (compressor.option("unsafe_undefined")) {
11521 var undef = find_scope(compressor).find_variable("undefined");
11523 var ref = make_node(AST_SymbolRef, self, {
11524 name : "undefined",
11525 scope : undef.scope,
11528 ref.is_undefined = true;
11532 var lhs = is_lhs(compressor.self(), compressor.parent());
11533 if (lhs && is_atomic(lhs, self)) return self;
11534 return make_node(AST_UnaryPrefix, self, {
11536 expression: make_node(AST_Number, self, {
11542 OPT(AST_Infinity, function(self, compressor) {
11543 var lhs = is_lhs(compressor.self(), compressor.parent());
11544 if (lhs && is_atomic(lhs, self)) return self;
11545 if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) {
11548 return make_node(AST_Binary, self, {
11550 left: make_node(AST_Number, self, {
11553 right: make_node(AST_Number, self, {
11559 OPT(AST_NaN, function(self, compressor) {
11560 var lhs = is_lhs(compressor.self(), compressor.parent());
11561 if (lhs && is_atomic(lhs, self)) return self;
11562 if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
11563 return make_node(AST_Binary, self, {
11565 left: make_node(AST_Number, self, {
11568 right: make_node(AST_Number, self, {
11574 function is_reachable(self, defs) {
11575 var reachable = false;
11576 var find_ref = new TreeWalker(function(node) {
11577 if (reachable) return true;
11578 if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
11579 return reachable = true;
11582 var scan_scope = new TreeWalker(function(node) {
11583 if (reachable) return true;
11584 if (node instanceof AST_Lambda && node !== self) {
11585 if (!(node.name || is_async(node) || is_generator(node))) {
11586 var parent = scan_scope.parent();
11587 if (parent instanceof AST_Call && parent.expression === node) return;
11589 node.walk(find_ref);
11593 self.walk(scan_scope);
11597 var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
11598 var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
11599 OPT(AST_Assign, function(self, compressor) {
11600 if (compressor.option("dead_code")) {
11601 if (self.left instanceof AST_PropAccess) {
11602 if (self.operator == "=") {
11604 var exprs = [ self.left.expression ];
11605 if (self.left instanceof AST_Sub) exprs.push(self.left.property);
11606 exprs.push(self.right);
11607 return make_sequence(self, exprs).optimize(compressor);
11609 if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
11612 var exp = self.left.expression;
11613 if (exp instanceof AST_Lambda
11614 || !compressor.has_directive("use strict")
11615 && exp instanceof AST_Constant
11616 && !exp.may_throw_on_access(compressor)) {
11617 return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
11618 self.left.property,
11620 ]).optimize(compressor);
11623 } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) {
11625 if (self.operator == "=" && self.left.equivalent_to(self.right)
11626 && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11629 if (self.left.is_immutable()) return strip_assignment();
11630 var def = self.left.definition();
11631 var scope = def.scope.resolve();
11632 var local = scope === compressor.find_parent(AST_Lambda);
11633 var level = 0, node;
11634 parent = compressor.self();
11635 if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do {
11637 parent = compressor.parent(level++);
11638 if (parent instanceof AST_Assign) {
11639 if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) {
11640 if (in_try(level, parent)) break;
11641 return strip_assignment(def);
11643 if (parent.left.match_symbol(function(node) {
11644 if (node instanceof AST_PropAccess) return true;
11648 if (parent instanceof AST_Exit) {
11650 if (in_try(level, parent)) break;
11651 if (is_reachable(scope, [ def ])) break;
11652 return strip_assignment(def);
11654 if (parent instanceof AST_SimpleStatement) {
11656 if (is_reachable(scope, [ def ])) break;
11660 parent = compressor.parent(level++);
11661 if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def);
11662 } while (is_tail_block(stat, parent));
11665 if (parent instanceof AST_VarDef) {
11666 if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
11667 if (parent.name.definition() !== def) continue;
11668 if (in_try(level, parent)) break;
11669 return strip_assignment(def);
11671 } while (is_tail(node, parent));
11674 if (compressor.option("sequences")) {
11675 var seq = self.lift_sequences(compressor);
11676 if (seq !== self) return seq.optimize(compressor);
11678 if (compressor.option("assignments")) {
11679 if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
11680 // x = expr1 OP expr2
11681 if (self.right.left instanceof AST_SymbolRef
11682 && self.right.left.name == self.left.name
11683 && ASSIGN_OPS[self.right.operator]) {
11684 // x = x - 2 ---> x -= 2
11685 return make_node(AST_Assign, self, {
11686 operator: self.right.operator + "=",
11688 right: self.right.right,
11691 if (self.right.right instanceof AST_SymbolRef
11692 && self.right.right.name == self.left.name
11693 && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
11694 && !self.right.left.has_side_effects(compressor)) {
11695 // x = 2 & x ---> x &= 2
11696 return make_node(AST_Assign, self, {
11697 operator: self.right.operator + "=",
11699 right: self.right.left,
11703 if ((self.operator == "-=" || self.operator == "+="
11704 && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
11705 && self.right instanceof AST_Number
11706 && self.right.value == 1) {
11707 var op = self.operator.slice(0, -1);
11708 return make_node(AST_UnaryPrefix, self, {
11710 expression: self.left
11714 return try_evaluate(compressor, self);
11716 function is_tail(node, parent) {
11717 if (parent instanceof AST_Binary) {
11718 return parent.right === node || parent.right.is_constant_expression(scope);
11720 if (parent instanceof AST_Conditional) {
11721 return parent.condition !== node
11722 || parent.consequent.is_constant_expression(scope)
11723 && parent.alternative.is_constant_expression(scope);
11725 if (parent instanceof AST_Sequence) {
11726 var exprs = parent.expressions;
11727 var stop = exprs.indexOf(node);
11728 if (stop < 0) return false;
11729 for (var i = exprs.length; --i > stop;) {
11730 if (!exprs[i].is_constant_expression(scope)) return false;
11734 if (parent instanceof AST_UnaryPrefix) return true;
11737 function is_tail_block(stat, parent) {
11738 if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat);
11739 if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat);
11740 if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat);
11741 if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat;
11742 if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
11745 function in_try(level, node) {
11746 var right = self.right;
11747 self.right = make_node(AST_Null, right);
11748 var may_throw = node.may_throw(compressor);
11749 self.right = right;
11750 for (var parent; parent = compressor.parent(level++); node = parent) {
11751 if (parent === scope) return false;
11752 if (parent instanceof AST_Try) {
11753 if (parent.bfinally && parent.bfinally !== node) return true;
11754 if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
11759 function strip_assignment(def) {
11760 if (def) def.fixed = false;
11761 return (self.operator != "=" ? make_node(AST_Binary, self, {
11762 operator: self.operator.slice(0, -1),
11765 }) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
11769 OPT(AST_Conditional, function(self, compressor) {
11770 if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
11771 var expressions = self.condition.expressions.slice();
11772 self.condition = expressions.pop();
11773 expressions.push(self);
11774 return make_sequence(self, expressions);
11776 if (!compressor.option("conditionals")) return self;
11777 var condition = self.condition;
11778 if (compressor.option("booleans") && !condition.has_side_effects(compressor)) {
11779 mark_duplicate_condition(compressor, condition);
11781 condition = fuzzy_eval(compressor, condition);
11783 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
11784 return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
11785 } else if (!(condition instanceof AST_Node)) {
11786 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
11787 return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
11789 var negated = condition.negate(compressor, first_in_statement(compressor));
11790 if (best_of(compressor, condition, negated) === negated) {
11791 self = make_node(AST_Conditional, self, {
11792 condition: negated,
11793 consequent: self.alternative,
11794 alternative: self.consequent
11796 negated = condition;
11797 condition = self.condition;
11799 var consequent = self.consequent;
11800 var alternative = self.alternative;
11801 if (repeatable(compressor, condition)) {
11802 // x ? x : y ---> x || y
11803 if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
11806 right: alternative,
11807 }).optimize(compressor);
11808 // x ? y : x ---> x && y
11809 if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
11813 }).optimize(compressor);
11815 // if (foo) exp = something; else exp = something_else;
11818 // exp = foo ? something : something_else;
11819 var seq_tail = consequent.tail_node();
11820 if (seq_tail instanceof AST_Assign) {
11821 var is_eq = seq_tail.operator == "=";
11822 var alt_tail = is_eq ? alternative.tail_node() : alternative;
11823 if ((is_eq || consequent === seq_tail)
11824 && alt_tail instanceof AST_Assign
11825 && seq_tail.operator == alt_tail.operator
11826 && seq_tail.left.equivalent_to(alt_tail.left)
11827 && (is_eq && seq_tail.left instanceof AST_SymbolRef
11828 || !condition.has_side_effects(compressor)
11829 && can_shift_lhs_of_tail(consequent)
11830 && can_shift_lhs_of_tail(alternative))) {
11831 return make_node(AST_Assign, self, {
11832 operator: seq_tail.operator,
11833 left: seq_tail.left,
11834 right: make_node(AST_Conditional, self, {
11835 condition: condition,
11836 consequent: pop_lhs(consequent),
11837 alternative: pop_lhs(alternative)
11842 // x ? y : y ---> x, y
11843 if (consequent.equivalent_to(alternative)) return make_sequence(self, [
11846 ]).optimize(compressor);
11847 // x ? y.p : z.p ---> (x ? y : z).p
11848 // x ? y(a) : z(a) ---> (x ? y : z)(a)
11849 // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
11850 var combined = combine_tail(consequent, alternative, true);
11851 if (combined) return combined;
11852 // x ? y(a) : y(b) ---> y(x ? a : b)
11854 if (consequent instanceof AST_Call
11855 && alternative.TYPE == consequent.TYPE
11856 && (arg_index = arg_diff(consequent, alternative)) >= 0
11857 && consequent.expression.equivalent_to(alternative.expression)
11858 && !condition.has_side_effects(compressor)
11859 && !consequent.expression.has_side_effects(compressor)) {
11860 var node = consequent.clone();
11861 var arg = consequent.args[arg_index];
11862 node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, {
11863 expression: make_node(AST_Conditional, self, {
11864 condition: condition,
11865 consequent: arg.expression,
11866 alternative: alternative.args[arg_index].expression,
11868 }) : make_node(AST_Conditional, self, {
11869 condition: condition,
11871 alternative: alternative.args[arg_index],
11875 // x ? (y ? a : b) : b ---> x && y ? a : b
11876 if (consequent instanceof AST_Conditional
11877 && consequent.alternative.equivalent_to(alternative)) {
11878 return make_node(AST_Conditional, self, {
11879 condition: make_node(AST_Binary, self, {
11882 right: consequent.condition
11884 consequent: consequent.consequent,
11885 alternative: alternative
11888 // x ? (y ? a : b) : a ---> !x || y ? a : b
11889 if (consequent instanceof AST_Conditional
11890 && consequent.consequent.equivalent_to(alternative)) {
11891 return make_node(AST_Conditional, self, {
11892 condition: make_node(AST_Binary, self, {
11895 right: consequent.condition
11897 consequent: alternative,
11898 alternative: consequent.alternative
11901 // x ? a : (y ? a : b) ---> x || y ? a : b
11902 if (alternative instanceof AST_Conditional
11903 && consequent.equivalent_to(alternative.consequent)) {
11904 return make_node(AST_Conditional, self, {
11905 condition: make_node(AST_Binary, self, {
11908 right: alternative.condition
11910 consequent: consequent,
11911 alternative: alternative.alternative
11914 // x ? b : (y ? a : b) ---> !x && y ? a : b
11915 if (alternative instanceof AST_Conditional
11916 && consequent.equivalent_to(alternative.alternative)) {
11917 return make_node(AST_Conditional, self, {
11918 condition: make_node(AST_Binary, self, {
11921 right: alternative.condition
11923 consequent: alternative.consequent,
11924 alternative: consequent
11927 // x ? (a, c) : (b, c) ---> x ? a : b, c
11928 if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
11929 && consequent.tail_node().equivalent_to(alternative.tail_node())) {
11930 return make_sequence(self, [
11931 make_node(AST_Conditional, self, {
11932 condition: condition,
11933 consequent: pop_seq(consequent),
11934 alternative: pop_seq(alternative)
11936 consequent.tail_node()
11937 ]).optimize(compressor);
11939 // x ? y && a : a ---> (!x || y) && a
11940 if (consequent instanceof AST_Binary
11941 && consequent.operator == "&&"
11942 && consequent.right.equivalent_to(alternative)) {
11943 return make_node(AST_Binary, self, {
11945 left: make_node(AST_Binary, self, {
11948 right: consequent.left
11951 }).optimize(compressor);
11953 // x ? y || a : a ---> x && y || a
11954 if (consequent instanceof AST_Binary
11955 && consequent.operator == "||"
11956 && consequent.right.equivalent_to(alternative)) {
11957 return make_node(AST_Binary, self, {
11959 left: make_node(AST_Binary, self, {
11962 right: consequent.left
11965 }).optimize(compressor);
11967 // x ? a : y && a ---> (x || y) && a
11968 if (alternative instanceof AST_Binary
11969 && alternative.operator == "&&"
11970 && alternative.right.equivalent_to(consequent)) {
11971 return make_node(AST_Binary, self, {
11973 left: make_node(AST_Binary, self, {
11976 right: alternative.left
11979 }).optimize(compressor);
11981 // x ? a : y || a ---> !x && y || a
11982 if (alternative instanceof AST_Binary
11983 && alternative.operator == "||"
11984 && alternative.right.equivalent_to(consequent)) {
11985 return make_node(AST_Binary, self, {
11987 left: make_node(AST_Binary, self, {
11990 right: alternative.left
11993 }).optimize(compressor);
11995 var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
11996 if (is_true(consequent)) {
11997 if (is_false(alternative)) {
11998 // c ? true : false ---> !!c
11999 return booleanize(condition);
12001 // c ? true : x ---> !!c || x
12002 return make_node(AST_Binary, self, {
12004 left: booleanize(condition),
12008 if (is_false(consequent)) {
12009 if (is_true(alternative)) {
12010 // c ? false : true ---> !c
12011 return booleanize(condition.negate(compressor));
12013 // c ? false : x ---> !c && x
12014 return make_node(AST_Binary, self, {
12016 left: booleanize(condition.negate(compressor)),
12020 if (is_true(alternative)) {
12021 // c ? x : true ---> !c || x
12022 return make_node(AST_Binary, self, {
12024 left: booleanize(condition.negate(compressor)),
12028 if (is_false(alternative)) {
12029 // c ? x : false ---> !!c && x
12030 return make_node(AST_Binary, self, {
12032 left: booleanize(condition),
12036 if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
12039 function booleanize(node) {
12040 if (node.is_boolean(compressor)) return node;
12042 return make_node(AST_UnaryPrefix, node, {
12044 expression: node.negate(compressor)
12049 function is_true(node) {
12050 return node instanceof AST_True
12052 && node instanceof AST_Constant
12054 || (node instanceof AST_UnaryPrefix
12055 && node.operator == "!"
12056 && node.expression instanceof AST_Constant
12057 && !node.expression.value);
12059 // AST_False or !1 or void 0
12060 function is_false(node) {
12061 return node instanceof AST_False
12063 && (node instanceof AST_Constant
12065 || node instanceof AST_UnaryPrefix
12066 && node.operator == "void"
12067 && !node.expression.has_side_effects(compressor))
12068 || (node instanceof AST_UnaryPrefix
12069 && node.operator == "!"
12070 && node.expression instanceof AST_Constant
12071 && node.expression.value);
12074 function arg_diff(consequent, alternative) {
12075 var a = consequent.args;
12076 var b = alternative.args;
12077 var len = a.length;
12078 if (len != b.length) return -2;
12079 for (var i = 0; i < len; i++) {
12080 if (!a[i].equivalent_to(b[i])) {
12081 if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
12082 for (var j = i + 1; j < len; j++) {
12083 if (!a[j].equivalent_to(b[j])) return -2;
12091 function is_tail_equivalent(consequent, alternative) {
12092 if (consequent.TYPE != alternative.TYPE) return;
12093 if (consequent.optional != alternative.optional) return;
12094 if (consequent instanceof AST_Call) {
12095 if (arg_diff(consequent, alternative) != -1) return;
12096 return consequent.TYPE != "Call"
12097 || !(consequent.expression instanceof AST_PropAccess
12098 || alternative.expression instanceof AST_PropAccess)
12099 || is_tail_equivalent(consequent.expression, alternative.expression);
12101 if (!(consequent instanceof AST_PropAccess)) return;
12102 var p = consequent.property;
12103 var q = alternative.property;
12104 return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
12105 && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
12108 function combine_tail(consequent, alternative, top) {
12109 if (!is_tail_equivalent(consequent, alternative)) return !top && make_node(AST_Conditional, self, {
12110 condition: condition,
12111 consequent: consequent,
12112 alternative: alternative,
12114 var node = consequent.clone();
12115 node.expression = combine_tail(consequent.expression, alternative.expression);
12119 function can_shift_lhs_of_tail(node) {
12120 return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
12121 return !expr.has_side_effects(compressor);
12125 function pop_lhs(node) {
12126 if (!(node instanceof AST_Sequence)) return node.right;
12127 var exprs = node.expressions.slice();
12128 exprs.push(exprs.pop().right);
12129 return make_sequence(node, exprs);
12132 function pop_seq(node) {
12133 if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
12136 return make_sequence(node, node.expressions.slice(0, -1));
12140 OPT(AST_Boolean, function(self, compressor) {
12141 if (!compressor.option("booleans")) return self;
12142 if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
12145 var p = compressor.parent();
12146 if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
12147 AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
12148 operator : p.operator,
12149 value : self.value,
12150 file : p.start.file,
12151 line : p.start.line,
12154 return make_node(AST_Number, self, {
12158 return make_node(AST_UnaryPrefix, self, {
12160 expression: make_node(AST_Number, self, {
12161 value: 1 - self.value
12166 OPT(AST_Spread, function(self, compressor) {
12167 var exp = self.expression;
12168 if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
12169 return List.splice(exp.elements.map(function(node) {
12170 return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
12176 function safe_to_flatten(value, compressor) {
12177 if (!value) return false;
12178 var parent = compressor.parent();
12179 if (parent.TYPE != "Call") return true;
12180 if (parent.expression !== compressor.self()) return true;
12181 if (value instanceof AST_SymbolRef) {
12182 value = value.fixed_value();
12183 if (!value) return false;
12185 return value instanceof AST_Lambda && !value.contains_this();
12188 OPT(AST_Sub, function(self, compressor) {
12189 var expr = self.expression;
12190 var prop = self.property;
12191 var terminated = trim_optional_chain(self, compressor);
12192 if (terminated) return terminated;
12193 if (compressor.option("properties")) {
12194 var key = prop.evaluate(compressor);
12195 if (key !== prop) {
12196 if (typeof key == "string") {
12197 if (key == "undefined") {
12200 var value = parseFloat(key);
12201 if (value.toString() == key) {
12206 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
12207 var property = "" + key;
12208 if (is_identifier_string(property)
12209 && property.length <= prop.print_to_string().length + 1) {
12210 return make_node(AST_Dot, self, {
12211 optional: self.optional,
12213 property: property,
12214 }).optimize(compressor);
12218 var parent = compressor.parent();
12219 var assigned = is_lhs(compressor.self(), parent);
12220 var def, fn, fn_parent, index;
12221 if (compressor.option("arguments")
12222 && expr instanceof AST_SymbolRef
12223 && is_arguments(def = expr.definition())
12225 && prop instanceof AST_Number
12226 && Math.floor(index = prop.value) == index
12227 && (fn = def.scope) === find_lambda()
12228 && fn.uses_arguments < (assigned ? 2 : 3)) {
12229 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
12230 if (!def.deleted) def.deleted = [];
12231 def.deleted[index] = true;
12233 var argname = fn.argnames[index];
12234 if (def.deleted && def.deleted[index]) {
12236 } else if (argname) {
12238 if (!(argname instanceof AST_SymbolFunarg)
12239 || argname.name == "await"
12240 || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) {
12242 } else if (compressor.has_directive("use strict")
12245 || !(fn_parent instanceof AST_Call
12246 && index < fn_parent.args.length
12247 && all(fn_parent.args.slice(0, index + 1), function(arg) {
12248 return !(arg instanceof AST_Spread);
12250 || !all(fn.argnames, function(argname) {
12251 return argname instanceof AST_SymbolFunarg;
12253 if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
12255 } else if ((assigned || !has_reassigned())
12256 && index < fn.argnames.length + 5
12257 && compressor.drop_fargs(fn, fn_parent)
12259 while (index >= fn.argnames.length) {
12260 argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
12261 fn.argnames.push(argname);
12264 if (argname && find_if(function(node) {
12265 return node.name === argname.name;
12266 }, fn.argnames) === argname) {
12267 if (assigned) def.reassigned--;
12268 var sym = make_node(AST_SymbolRef, self, argname);
12270 delete argname.__unused;
12274 if (assigned) return self;
12275 if (compressor.option("sequences")
12276 && parent.TYPE != "Call"
12277 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12278 var seq = lift_sequence_in_expression(self, compressor);
12279 if (seq !== self) return seq.optimize(compressor);
12281 if (key !== prop) {
12282 var sub = self.flatten_object(property, compressor);
12284 expr = self.expression = sub.expression;
12285 prop = self.property = sub.property;
12289 if (compressor.option("properties")
12290 && compressor.option("side_effects")
12291 && prop instanceof AST_Number
12292 && expr instanceof AST_Array
12293 && all(elements = expr.elements, function(value) {
12294 return !(value instanceof AST_Spread);
12296 var index = prop.value;
12297 var retValue = elements[index];
12298 if (safe_to_flatten(retValue, compressor)) {
12299 var is_hole = retValue instanceof AST_Hole;
12300 var flatten = !is_hole;
12302 for (var i = elements.length; --i > index;) {
12303 var value = elements[i].drop_side_effect_free(compressor);
12305 values.unshift(value);
12306 if (flatten && value.has_side_effects(compressor)) flatten = false;
12309 if (!flatten) values.unshift(retValue);
12311 var value = elements[i].drop_side_effect_free(compressor);
12313 values.unshift(value);
12314 } else if (is_hole) {
12315 values.unshift(make_node(AST_Hole, elements[i]));
12321 values.push(retValue);
12322 return make_sequence(self, values).optimize(compressor);
12323 } else return make_node(AST_Sub, self, {
12324 expression: make_node(AST_Array, expr, { elements: values }),
12325 property: make_node(AST_Number, prop, { value: index }),
12329 return try_evaluate(compressor, self);
12331 function find_lambda() {
12333 while (p = compressor.parent(i++)) {
12334 if (p instanceof AST_Lambda) {
12335 if (p instanceof AST_Accessor) return;
12336 if (is_arrow(p)) continue;
12337 fn_parent = compressor.parent(i);
12343 function has_reassigned() {
12344 return !compressor.option("reduce_vars") || def.reassigned;
12348 AST_Arrow.DEFMETHOD("contains_super", return_false);
12349 AST_AsyncArrow.DEFMETHOD("contains_super", return_false);
12350 AST_Lambda.DEFMETHOD("contains_super", function() {
12353 self.walk(new TreeWalker(function(node) {
12354 if (result) return true;
12355 if (node instanceof AST_Super) return result = true;
12356 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12360 AST_LambdaDefinition.DEFMETHOD("contains_super", return_false);
12361 AST_Scope.DEFMETHOD("contains_super", return_false);
12363 AST_Arrow.DEFMETHOD("contains_this", return_false);
12364 AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
12365 AST_Node.DEFMETHOD("contains_this", function() {
12368 self.walk(new TreeWalker(function(node) {
12369 if (result) return true;
12370 if (node instanceof AST_This) return result = true;
12371 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12376 function can_hoist_property(prop) {
12377 return prop instanceof AST_ObjectKeyVal
12378 && typeof prop.key == "string"
12379 && !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
12382 AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
12383 if (!compressor.option("properties")) return;
12384 if (key === "__proto__") return;
12385 var expr = this.expression;
12386 if (expr instanceof AST_Object) {
12387 var props = expr.properties;
12388 for (var i = props.length; --i >= 0;) {
12389 var prop = props[i];
12390 if (prop.key !== key) continue;
12391 if (!all(props, can_hoist_property)) return;
12392 if (!safe_to_flatten(prop.value, compressor)) return;
12393 var scope, values = [];
12394 for (var j = 0; j < props.length; j++) {
12395 var value = props[j].value;
12396 if (props[j] instanceof AST_ObjectMethod) {
12397 var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
12399 if (!scope) scope = compressor.find_parent(AST_Scope);
12400 var avoid = avoid_await_yield(scope);
12401 value.each_argname(function(argname) {
12402 if (avoid[argname.name]) arrow = false;
12407 ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow;
12408 } else if (i === j && !(compressor.parent() instanceof AST_Call)) {
12413 value = make_node(ctor, value, value);
12415 values.push(value);
12417 return make_node(AST_Sub, this, {
12418 expression: make_node(AST_Array, expr, { elements: values }),
12419 property: make_node(AST_Number, this, { value: i }),
12425 OPT(AST_Dot, function(self, compressor) {
12426 if (self.property == "arguments" || self.property == "caller") {
12427 AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
12428 prop: self.property,
12429 file: self.start.file,
12430 line: self.start.line,
12431 col: self.start.col,
12434 var parent = compressor.parent();
12435 if (is_lhs(compressor.self(), parent)) return self;
12436 var terminated = trim_optional_chain(self, compressor);
12437 if (terminated) return terminated;
12438 if (compressor.option("sequences")
12439 && parent.TYPE != "Call"
12440 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12441 var seq = lift_sequence_in_expression(self, compressor);
12442 if (seq !== self) return seq.optimize(compressor);
12444 if (compressor.option("unsafe_proto")
12445 && self.expression instanceof AST_Dot
12446 && self.expression.property == "prototype") {
12447 var exp = self.expression.expression;
12448 if (is_undeclared_ref(exp)) switch (exp.name) {
12450 self.expression = make_node(AST_Array, self.expression, {
12455 self.expression = make_node(AST_Function, self.expression, {
12458 }).init_vars(exp.scope);
12461 self.expression = make_node(AST_Number, self.expression, {
12466 self.expression = make_node(AST_Object, self.expression, {
12471 self.expression = make_node(AST_RegExp, self.expression, {
12476 self.expression = make_node(AST_String, self.expression, {
12482 var sub = self.flatten_object(self.property, compressor);
12483 if (sub) return sub.optimize(compressor);
12484 return try_evaluate(compressor, self);
12487 OPT(AST_DestructuredArray, function(self, compressor) {
12488 if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) {
12489 return make_node(AST_DestructuredArray, self, {
12490 elements: self.elements.concat(self.rest.elements),
12491 rest: self.rest.rest,
12497 OPT(AST_DestructuredKeyVal, function(self, compressor) {
12498 if (compressor.option("objects")) {
12499 var key = self.key;
12500 if (key instanceof AST_Node) {
12501 key = key.evaluate(compressor);
12502 if (key !== self.key) self.key = "" + key;
12508 OPT(AST_Object, function(self, compressor) {
12509 if (!compressor.option("objects")) return self;
12510 var changed = false;
12512 var generated = false;
12513 var keep_duplicate = compressor.has_directive("use strict");
12514 var keys = new Dictionary();
12516 self.properties.forEach(function(prop) {
12517 if (!(prop instanceof AST_Spread)) return process(prop);
12519 var exp = prop.expression;
12520 if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
12521 if (prop instanceof AST_ObjectGetter) return false;
12522 if (prop instanceof AST_Spread) return false;
12523 if (prop.key !== "__proto__") return true;
12524 if (prop instanceof AST_ObjectSetter) return true;
12525 return !prop.value.has_side_effects(compressor);
12528 exp.properties.forEach(function(prop) {
12529 var key = prop.key;
12530 var setter = prop instanceof AST_ObjectSetter;
12531 if (key === "__proto__") {
12532 if (!setter) return;
12533 key = make_node_from_constant(key, prop);
12535 process(setter ? make_node(AST_ObjectKeyVal, prop, {
12537 value: make_node(AST_Undefined, prop).optimize(compressor),
12547 if (!changed) return self;
12548 if (found && generated && values.length == 1) {
12549 var value = values[0];
12550 if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
12551 value.key = "" + value.key.value;
12554 return make_node(AST_Object, self, { properties: values });
12557 keys.each(function(props) {
12558 if (props.length == 1) return values.push(props[0]);
12560 var tail = keep_duplicate && !generated && props.pop();
12561 values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
12563 value: make_sequence(self, props.map(function(prop) {
12567 if (tail) values.push(tail);
12569 keys = new Dictionary();
12572 function process(prop) {
12573 var key = prop.key;
12574 if (key instanceof AST_Node) {
12576 key = key.evaluate(compressor);
12577 if (key === prop.key || key === "__proto__") {
12580 key = prop.key = "" + key;
12583 if (can_hoist_property(prop)) {
12584 if (prop.value.has_side_effects(compressor)) flush();
12585 keys.add(key, prop);
12590 if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
12592 if (keys.has(key)) prop = keys.get(key)[0];
12593 prop.key = make_node(AST_Number, prop, { value: +key });
12598 OPT(AST_Return, function(self, compressor) {
12599 if (compressor.option("side_effects")
12601 && is_undefined(self.value, compressor)
12602 && !in_async_generator(compressor.find_parent(AST_Scope))) {
12607 })(function(node, optimizer) {
12608 node.DEFMETHOD("optimize", function(compressor) {
12610 if (self._optimized) return self;
12611 if (compressor.has_directive("use asm")) return self;
12612 var opt = optimizer(self, compressor);
12613 opt._optimized = true;