blob: 08b0f0d1cd53216e4d079e41697654ac59290108 [file] [log] [blame]
Tim van der Lippe652ccb72021-05-27 17:07:12 +01001/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS
5
6 -------------------------------- (C) ---------------------------------
7
8 Author: Mihai Bazon
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
11
12 Distributed under the BSD license:
13
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
18 are met:
19
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
22 disclaimer.
23
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
28
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 SUCH DAMAGE.
41
42 ***********************************************************************/
43
44"use strict";
45
46function Compressor(options, false_by_default) {
47 if (!(this instanceof Compressor))
48 return new Compressor(options, false_by_default);
49 TreeTransformer.call(this, this.before, this.after);
50 this.options = defaults(options, {
51 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,
63 drop_console : false,
64 drop_debugger : !false_by_default,
65 evaluate : !false_by_default,
66 expression : false,
67 functions : !false_by_default,
68 global_defs : false,
69 hoist_exports : !false_by_default,
70 hoist_funs : false,
71 hoist_props : !false_by_default,
72 hoist_vars : false,
73 ie8 : false,
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,
79 keep_fnames : false,
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,
86 passes : 1,
87 properties : !false_by_default,
88 pure_funcs : null,
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,
99 top_retain : null,
100 toplevel : !!(options && options["top_retain"]),
101 typeofs : !false_by_default,
102 unsafe : false,
103 unsafe_comps : false,
104 unsafe_Function : false,
105 unsafe_math : false,
106 unsafe_proto : false,
107 unsafe_regexp : false,
108 unsafe_undefined: false,
109 unused : !false_by_default,
110 varify : !false_by_default,
111 yields : !false_by_default,
112 }, true);
113 var evaluate = this.options["evaluate"];
114 this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
115 var global_defs = this.options["global_defs"];
116 if (typeof global_defs == "object") for (var key in global_defs) {
117 if (/^@/.test(key) && HOP(global_defs, key)) {
118 global_defs[key.slice(1)] = parse(global_defs[key], {
119 expression: true
120 });
121 }
122 }
123 if (this.options["inline"] === true) this.options["inline"] = 3;
124 this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) {
125 if (lambda.length_read) return false;
126 var name = lambda.name;
127 if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
128 if (name.fixed_value() !== lambda) return false;
129 var def = name.definition();
130 if (def.direct_access) return false;
131 var escaped = def.escaped;
132 return escaped && escaped.depth != 1;
133 };
134 var pure_funcs = this.options["pure_funcs"];
135 if (typeof pure_funcs == "function") {
136 this.pure_funcs = pure_funcs;
137 } else if (typeof pure_funcs == "string") {
138 this.pure_funcs = function(node) {
139 var expr;
140 if (node instanceof AST_Call) {
141 expr = node.expression;
142 } else if (node instanceof AST_Template) {
143 expr = node.tag;
144 }
145 return !(expr && pure_funcs === expr.print_to_string());
146 };
147 } else if (Array.isArray(pure_funcs)) {
148 this.pure_funcs = function(node) {
149 var expr;
150 if (node instanceof AST_Call) {
151 expr = node.expression;
152 } else if (node instanceof AST_Template) {
153 expr = node.tag;
154 }
155 return !(expr && member(expr.print_to_string(), pure_funcs));
156 };
157 } else {
158 this.pure_funcs = return_true;
159 }
160 var sequences = this.options["sequences"];
161 this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
162 var top_retain = this.options["top_retain"];
163 if (top_retain instanceof RegExp) {
164 this.top_retain = function(def) {
165 return top_retain.test(def.name);
166 };
167 } else if (typeof top_retain == "function") {
168 this.top_retain = top_retain;
169 } else if (top_retain) {
170 if (typeof top_retain == "string") {
171 top_retain = top_retain.split(/,/);
172 }
173 this.top_retain = function(def) {
174 return member(def.name, top_retain);
175 };
176 }
177 var toplevel = this.options["toplevel"];
178 this.toplevel = typeof toplevel == "string" ? {
179 funcs: /funcs/.test(toplevel),
180 vars: /vars/.test(toplevel)
181 } : {
182 funcs: toplevel,
183 vars: toplevel
184 };
185}
186
187Compressor.prototype = new TreeTransformer;
188merge(Compressor.prototype, {
189 option: function(key) { return this.options[key] },
190 exposed: function(def) {
191 if (def.exported) return true;
192 if (def.undeclared) return true;
193 if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
194 var toplevel = this.toplevel;
195 return !all(def.orig, function(sym) {
196 return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
197 });
198 },
199 compress: function(node) {
200 node = node.resolve_defines(this);
201 node.hoist_exports(this);
202 if (this.option("expression")) {
203 node.process_expression(true);
204 }
205 var passes = +this.options.passes || 1;
206 var min_count = 1 / 0;
207 var stopping = false;
208 var mangle = { ie8: this.option("ie8") };
209 for (var pass = 0; pass < passes; pass++) {
210 node.figure_out_scope(mangle);
211 if (pass > 0 || this.option("reduce_vars"))
212 node.reset_opt_flags(this);
213 node = node.transform(this);
214 if (passes > 1) {
215 var count = 0;
216 node.walk(new TreeWalker(function() {
217 count++;
218 }));
219 AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
220 pass: pass,
221 min_count: min_count,
222 count: count,
223 });
224 if (count < min_count) {
225 min_count = count;
226 stopping = false;
227 } else if (stopping) {
228 break;
229 } else {
230 stopping = true;
231 }
232 }
233 }
234 if (this.option("expression")) {
235 node.process_expression(false);
236 }
237 return node;
238 },
239 before: function(node, descend, in_list) {
240 if (node._squeezed) return node;
241 var is_scope = node instanceof AST_Scope;
242 if (is_scope) {
243 node.hoist_properties(this);
244 node.hoist_declarations(this);
245 node.process_boolean_returns(this);
246 }
247 // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
248 // would call AST_Node.transform() if a different instance of AST_Node is
249 // produced after OPT().
250 // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
251 // Migrate and defer all children's AST_Node.transform() to below, which
252 // will now happen after this parent AST_Node has been properly substituted
253 // thus gives a consistent AST snapshot.
254 descend(node, this);
255 // Existing code relies on how AST_Node.optimize() worked, and omitting the
256 // following replacement call would result in degraded efficiency of both
257 // output and performance.
258 descend(node, this);
259 var opt = node.optimize(this);
260 if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
261 opt.merge_variables(this);
262 opt.drop_unused(this);
263 descend(opt, this);
264 }
265 if (opt === node) opt._squeezed = true;
266 return opt;
267 }
268});
269
270(function(OPT) {
271 OPT(AST_Node, function(self, compressor) {
272 return self;
273 });
274
275 AST_Node.DEFMETHOD("equivalent_to", function(node) {
276 return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
277 });
278
279 AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) {
280 if (!compressor.option("hoist_exports")) return;
281 var body = this.body, props = [];
282 for (var i = 0; i < body.length; i++) {
283 var stat = body[i];
284 if (stat instanceof AST_ExportDeclaration) {
285 body[i] = stat = stat.body;
286 if (stat instanceof AST_Definitions) {
287 stat.definitions.forEach(function(defn) {
288 defn.name.match_symbol(export_symbol, true);
289 });
290 } else {
291 export_symbol(stat.name);
292 }
293 } else if (stat instanceof AST_ExportReferences) {
294 body.splice(i--, 1);
295 [].push.apply(props, stat.properties);
296 }
297 }
298 if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props }));
299
300 function export_symbol(sym) {
301 if (!(sym instanceof AST_SymbolDeclaration)) return;
302 var node = make_node(AST_SymbolExport, sym, sym);
303 node.alias = node.name;
304 props.push(node);
305 }
306 });
307
308 AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
309 var self = this;
310 var tt = new TreeTransformer(function(node) {
311 if (insert && node instanceof AST_SimpleStatement) {
312 return make_node(AST_Return, node, {
313 value: node.body
314 });
315 }
316 if (!insert && node instanceof AST_Return) {
317 return transform ? transform(node) : make_node(AST_SimpleStatement, node, {
318 body: node.value || make_node(AST_UnaryPrefix, node, {
319 operator: "void",
320 expression: make_node(AST_Number, node, {
321 value: 0
322 })
323 })
324 });
325 }
326 if (node instanceof AST_Lambda && node !== self) {
327 return node;
328 }
329 if (node instanceof AST_Block) {
330 var index = node.body.length - 1;
331 if (index >= 0) {
332 node.body[index] = node.body[index].transform(tt);
333 }
334 } else if (node instanceof AST_If) {
335 node.body = node.body.transform(tt);
336 if (node.alternative) {
337 node.alternative = node.alternative.transform(tt);
338 }
339 } else if (node instanceof AST_With) {
340 node.body = node.body.transform(tt);
341 }
342 return node;
343 });
344 self.transform(tt);
345 });
346
347 function read_property(obj, node) {
348 var key = node.getProperty();
349 if (key instanceof AST_Node) return;
350 var value;
351 if (obj instanceof AST_Array) {
352 var elements = obj.elements;
353 if (key == "length") return make_node_from_constant(elements.length, obj);
354 if (typeof key == "number" && key in elements) value = elements[key];
355 } else if (obj instanceof AST_Lambda) {
356 if (key == "length") {
357 obj.length_read = true;
358 return make_node_from_constant(obj.argnames.length, obj);
359 }
360 } else if (obj instanceof AST_Object) {
361 key = "" + key;
362 var props = obj.properties;
363 for (var i = props.length; --i >= 0;) {
364 var prop = props[i];
365 if (!can_hoist_property(prop)) return;
366 if (!value && props[i].key === key) value = props[i].value;
367 }
368 }
369 return value instanceof AST_SymbolRef && value.fixed_value() || value;
370 }
371
372 function is_read_only_fn(value, name) {
373 if (value instanceof AST_Boolean) return native_fns.Boolean[name];
374 if (value instanceof AST_Number) return native_fns.Number[name];
375 if (value instanceof AST_String) return native_fns.String[name];
376 if (name == "valueOf") return false;
377 if (value instanceof AST_Array) return native_fns.Array[name];
378 if (value instanceof AST_Lambda) return native_fns.Function[name];
379 if (value instanceof AST_Object) return native_fns.Object[name];
380 if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
381 }
382
383 function is_modified(compressor, tw, node, value, level, immutable, recursive) {
384 var parent = tw.parent(level);
385 if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
386 return;
387 }
388 var lhs = is_lhs(node, parent);
389 if (lhs) return lhs;
390 if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
391 if (parent instanceof AST_Binary) {
392 if (!lazy_op[parent.operator]) return;
393 return is_modified(compressor, tw, parent, parent, level + 1);
394 }
395 if (parent instanceof AST_Call) {
396 return !immutable
397 && parent.expression === node
398 && !parent.is_expr_pure(compressor)
399 && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
400 }
401 if (parent instanceof AST_Conditional) {
402 if (parent.condition === node) return;
403 return is_modified(compressor, tw, parent, parent, level + 1);
404 }
405 if (parent instanceof AST_ForEnumeration) return parent.init === node;
406 if (parent instanceof AST_ObjectKeyVal) {
407 if (parent.value !== node) return;
408 var obj = tw.parent(level + 1);
409 return is_modified(compressor, tw, obj, obj, level + 2);
410 }
411 if (parent instanceof AST_PropAccess) {
412 if (parent.expression !== node) return;
413 var prop = read_property(value, parent);
414 return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
415 }
416 if (parent instanceof AST_Sequence) {
417 if (parent.tail_node() !== node) return;
418 return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
419 }
420 }
421
422 function is_lambda(node) {
423 return node instanceof AST_Class || node instanceof AST_Lambda;
424 }
425
426 function safe_for_extends(node) {
427 return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function;
428 }
429
430 function is_arguments(def) {
431 return def.name == "arguments" && def.scope.uses_arguments;
432 }
433
434 function is_funarg(def) {
435 return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg;
436 }
437
438 function cross_scope(def, sym) {
439 do {
440 if (def === sym) return false;
441 if (sym instanceof AST_Scope) return true;
442 } while (sym = sym.parent_scope);
443 }
444
445 function can_drop_symbol(ref, compressor, keep_lambda) {
446 var def = ref.definition();
447 if (ref.in_arg && is_funarg(def)) return false;
448 return all(def.orig, function(sym) {
449 if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) {
450 return compressor && can_varify(compressor, sym);
451 }
452 return !(keep_lambda && sym instanceof AST_SymbolLambda);
453 });
454 }
455
456 var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
457 (function(def) {
458 def(AST_Node, noop);
459
460 function reset_def(tw, compressor, def) {
461 def.assignments = 0;
462 def.bool_fn = 0;
463 def.cross_loop = false;
464 def.direct_access = false;
465 def.escaped = [];
466 def.fixed = !def.const_redefs
467 && !def.scope.pinned()
468 && !compressor.exposed(def)
469 && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope)
470 && def.init;
471 if (def.fixed instanceof AST_LambdaDefinition && !all(def.references, function(ref) {
472 var scope = ref.scope.resolve();
473 do {
474 if (def.scope === scope) return true;
475 } while (scope instanceof AST_LambdaExpression && (scope = scope.parent_scope.resolve()));
476 })) {
477 tw.defun_ids[def.id] = false;
478 }
479 def.reassigned = 0;
480 def.recursive_refs = 0;
481 def.references = [];
482 def.should_replace = undefined;
483 def.single_use = undefined;
484 }
485
486 function reset_variables(tw, compressor, scope) {
487 scope.variables.each(function(def) {
488 reset_def(tw, compressor, def);
489 if (def.fixed === null) {
490 def.safe_ids = tw.safe_ids;
491 mark(tw, def);
492 } else if (def.fixed) {
493 tw.loop_ids[def.id] = tw.in_loop;
494 mark(tw, def);
495 }
496 });
497 scope.may_call_this = function() {
498 scope.may_call_this = noop;
499 if (!scope.contains_this()) return;
500 scope.functions.each(function(def) {
501 if (def.init instanceof AST_LambdaDefinition && !(def.id in tw.defun_ids)) {
502 tw.defun_ids[def.id] = false;
503 }
504 });
505 };
506 if (scope.uses_arguments) scope.each_argname(function(node) {
507 node.definition().last_ref = false;
508 });
509 if (compressor.option("ie8")) scope.variables.each(function(def) {
510 var d = def.orig[0].definition();
511 if (d !== def) d.fixed = false;
512 });
513 }
514
515 function mark_defun(tw, def) {
516 if (def.id in tw.defun_ids) {
517 var marker = tw.defun_ids[def.id];
518 if (!marker) return;
519 var visited = tw.defun_visited[def.id];
520 if (marker === tw.safe_ids) {
521 if (!visited) return def.fixed;
522 } else if (visited) {
523 def.init.enclosed.forEach(function(d) {
524 if (def.init.variables.get(d.name) === d) return;
525 if (!safe_to_read(tw, d)) d.fixed = false;
526 });
527 } else {
528 tw.defun_ids[def.id] = false;
529 }
530 } else {
531 if (!tw.in_loop) {
532 tw.defun_ids[def.id] = tw.safe_ids;
533 return def.fixed;
534 }
535 tw.defun_ids[def.id] = false;
536 }
537 }
538
539 function walk_defuns(tw, scope) {
540 scope.functions.each(function(def) {
541 if (def.init instanceof AST_LambdaDefinition && !tw.defun_visited[def.id]) {
542 tw.defun_ids[def.id] = tw.safe_ids;
543 def.init.walk(tw);
544 }
545 });
546 }
547
548 function push(tw) {
549 tw.safe_ids = Object.create(tw.safe_ids);
550 }
551
552 function pop(tw) {
553 tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
554 }
555
556 function mark(tw, def) {
557 tw.safe_ids[def.id] = {};
558 }
559
560 function push_ref(def, ref) {
561 def.references.push(ref);
562 if (def.last_ref !== false) def.last_ref = ref;
563 }
564
565 function safe_to_read(tw, def) {
566 if (def.single_use == "m") return false;
567 var safe = tw.safe_ids[def.id];
568 if (safe) {
569 if (!HOP(tw.safe_ids, def.id)) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids;
570 if (def.fixed == null) {
571 if (is_arguments(def)) return false;
572 if (def.global && def.name == "arguments") return false;
573 tw.loop_ids[def.id] = null;
574 def.fixed = make_node(AST_Undefined, def.orig[0]);
575 return true;
576 }
577 return !safe.assign || safe.assign === tw.safe_ids;
578 }
579 return def.fixed instanceof AST_LambdaDefinition;
580 }
581
582 function safe_to_assign(tw, def, declare) {
583 if (!declare) {
584 if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false;
585 if (!all(def.orig, function(sym) {
586 return !(sym instanceof AST_SymbolConst);
587 })) return false;
588 }
589 if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
590 return !(sym instanceof AST_SymbolLet);
591 });
592 if (def.fixed === null && def.safe_ids) {
593 def.safe_ids[def.id] = false;
594 delete def.safe_ids;
595 return true;
596 }
597 if (def.fixed === false) return false;
598 var safe = tw.safe_ids[def.id];
599 if (!HOP(tw.safe_ids, def.id)) {
600 if (!safe) return false;
601 if (safe.read && def.scope !== tw.find_parent(AST_Scope)) return false;
602 safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
603 }
604 if (def.fixed != null && safe.read) {
605 if (safe.read !== tw.safe_ids) return false;
606 if (tw.loop_ids[def.id] !== tw.in_loop) return false;
607 }
608 return safe_to_read(tw, def) && all(def.orig, function(sym) {
609 return !(sym instanceof AST_SymbolLambda);
610 });
611 }
612
613 function make_ref(ref, fixed) {
614 var node = make_node(AST_SymbolRef, ref, ref);
615 node.fixed = fixed || make_node(AST_Undefined, ref);
616 return node;
617 }
618
619 function ref_once(compressor, def) {
620 return compressor.option("unused")
621 && !def.scope.pinned()
622 && def.single_use !== false
623 && def.references.length - def.recursive_refs == 1
624 && !(is_funarg(def) && def.scope.uses_arguments);
625 }
626
627 function is_immutable(value) {
628 if (!value) return false;
629 if (value instanceof AST_Assign) {
630 var op = value.operator;
631 return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)];
632 }
633 if (value instanceof AST_Sequence) return is_immutable(value.tail_node());
634 return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
635 }
636
637 function has_escaped(d, node, parent) {
638 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
639 if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
640 if (parent instanceof AST_Exit) return parent.value === node && node.scope !== d.scope;
641 if (parent instanceof AST_VarDef) return parent.value === node;
642 }
643
644 function value_in_use(node, parent) {
645 if (parent instanceof AST_Array) return true;
646 if (parent instanceof AST_Binary) return lazy_op[parent.operator];
647 if (parent instanceof AST_Conditional) return parent.condition !== node;
648 if (parent instanceof AST_Sequence) return parent.tail_node() === node;
649 if (parent instanceof AST_Spread) return true;
650 }
651
652 function mark_escaped(tw, d, scope, node, value, level, depth) {
653 var parent = tw.parent(level);
654 if (value && value.is_constant()) return;
655 if (has_escaped(d, node, parent)) {
656 d.escaped.push(parent);
657 if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
658 if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
659 return;
660 } else if (value_in_use(node, parent)) {
661 mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
662 } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
663 var obj = tw.parent(level + 1);
664 mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
665 } else if (parent instanceof AST_PropAccess && parent.expression === node) {
666 value = read_property(value, parent);
667 mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
668 if (value) return;
669 }
670 if (level > 0) return;
671 if (parent instanceof AST_Call && parent.expression === node) return;
672 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
673 if (parent instanceof AST_SimpleStatement) return;
674 if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
675 d.direct_access = true;
676 }
677
678 function mark_assignment_to_arguments(node) {
679 if (!(node instanceof AST_Sub)) return;
680 var expr = node.expression;
681 if (!(expr instanceof AST_SymbolRef)) return;
682 var def = expr.definition();
683 if (!is_arguments(def)) return;
684 var key = node.property;
685 if (key.is_constant()) key = key.value;
686 if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return;
687 def.reassigned++;
688 (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
689 if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
690 });
691 }
692
693 function scan_declaration(tw, compressor, lhs, fixed, visit) {
694 var scanner = new TreeWalker(function(node) {
695 if (node instanceof AST_DefaultValue) {
696 reset_flags(node);
697 push(tw);
698 node.value.walk(tw);
699 pop(tw);
700 var save = fixed;
701 if (save) fixed = function() {
702 var value = save();
703 return is_undefined(value) ? make_sequence(node, [ value, node.value ]) : node;
704 };
705 node.name.walk(scanner);
706 fixed = save;
707 return true;
708 }
709 if (node instanceof AST_DestructuredArray) {
710 reset_flags(node);
711 var save = fixed;
712 node.elements.forEach(function(node, index) {
713 if (node instanceof AST_Hole) return reset_flags(node);
714 if (save) fixed = function() {
715 return make_node(AST_Sub, node, {
716 expression: save(),
717 property: make_node(AST_Number, node, { value: index }),
718 });
719 };
720 node.walk(scanner);
721 });
722 if (node.rest) {
723 if (save) fixed = compressor.option("rests") && function() {
724 var value = save();
725 return value instanceof AST_Array ? make_node(AST_Array, node, {
726 elements: value.elements.slice(node.elements.length),
727 }) : node;
728 };
729 node.rest.walk(scanner);
730 }
731 fixed = save;
732 return true;
733 }
734 if (node instanceof AST_DestructuredObject) {
735 reset_flags(node);
736 var save = fixed;
737 node.properties.forEach(function(node) {
738 reset_flags(node);
739 if (node.key instanceof AST_Node) {
740 push(tw);
741 node.key.walk(tw);
742 pop(tw);
743 }
744 if (save) fixed = function() {
745 var key = node.key;
746 var type = AST_Sub;
747 if (typeof key == "string") {
748 if (is_identifier_string(key)) {
749 type = AST_Dot;
750 } else {
751 key = make_node_from_constant(key, node);
752 }
753 }
754 return make_node(type, node, {
755 expression: save(),
756 property: key
757 });
758 };
759 node.value.walk(scanner);
760 });
761 if (node.rest) {
762 fixed = false;
763 node.rest.walk(scanner);
764 }
765 fixed = save;
766 return true;
767 }
768 visit(node, fixed, function() {
769 var save_len = tw.stack.length;
770 for (var i = 0, len = scanner.stack.length - 1; i < len; i++) {
771 tw.stack.push(scanner.stack[i]);
772 }
773 node.walk(tw);
774 tw.stack.length = save_len;
775 });
776 return true;
777 });
778 lhs.walk(scanner);
779 }
780
781 function reduce_iife(tw, descend, compressor) {
782 var fn = this;
783 fn.inlined = false;
784 var iife = tw.parent();
785 var hit = is_async(fn) || is_generator(fn);
786 var aborts = false;
787 fn.walk(new TreeWalker(function(node) {
788 if (hit) return aborts = true;
789 if (node instanceof AST_Return) return hit = true;
790 if (node instanceof AST_Scope && node !== fn) return true;
791 }));
792 if (aborts) push(tw);
793 reset_variables(tw, compressor, fn);
794 // Virtually turn IIFE parameters into variable definitions:
795 // (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})()
796 // So existing transformation rules can work on them.
797 var safe = !fn.uses_arguments || tw.has_directive("use strict");
798 fn.argnames.forEach(function(argname, i) {
799 var value = iife.args[i];
800 scan_declaration(tw, compressor, argname, function() {
801 var j = fn.argnames.indexOf(argname);
802 var arg = j < 0 ? value : iife.args[j];
803 if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0];
804 return arg || make_node(AST_Undefined, iife);
805 }, visit);
806 });
807 var rest = fn.rest;
808 if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() {
809 return fn.rest === rest ? make_node(AST_Array, fn, {
810 elements: iife.args.slice(fn.argnames.length),
811 }) : rest;
812 }, visit);
813 walk_lambda(fn, tw);
814 var safe_ids = tw.safe_ids;
815 pop(tw);
816 walk_defuns(tw, fn);
817 if (!aborts) tw.safe_ids = safe_ids;
818 return true;
819
820 function visit(node, fixed) {
821 var d = node.definition();
822 if (fixed && safe && d.fixed === undefined) {
823 mark(tw, d);
824 tw.loop_ids[d.id] = tw.in_loop;
825 d.fixed = fixed;
826 d.fixed.assigns = [ node ];
827 } else {
828 d.fixed = false;
829 }
830 }
831 }
832
833 def(AST_Assign, function(tw, descend, compressor) {
834 var node = this;
835 var left = node.left;
836 var right = node.right;
837 var scan = left instanceof AST_Destructured || left instanceof AST_SymbolRef;
838 switch (node.operator) {
839 case "=":
840 if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
841 right.walk(tw);
842 walk_prop(left);
843 node.__drop = true;
844 return true;
845 }
846 if (scan) {
847 walk_assign();
848 return true;
849 }
850 mark_assignment_to_arguments(left);
851 return;
852 case "&&=":
853 case "||=":
854 case "??=":
855 left.walk(tw);
856 push(tw);
857 if (scan) {
858 walk_assign();
859 } else {
860 mark_assignment_to_arguments(left);
861 right.walk(tw);
862 }
863 pop(tw);
864 return true;
865 default:
866 if (!scan) {
867 mark_assignment_to_arguments(left);
868 return;
869 }
870 var d = left.definition();
871 d.assignments++;
872 var fixed = d.fixed;
873 if (is_modified(compressor, tw, node, node, 0)) {
874 d.fixed = false;
875 return;
876 }
877 var safe = safe_to_read(tw, d);
878 right.walk(tw);
879 if (safe && !left.in_arg && safe_to_assign(tw, d)) {
880 push_ref(d, left);
881 mark(tw, d);
882 if (d.single_use) d.single_use = false;
883 left.fixed = d.fixed = function() {
884 return make_node(AST_Binary, node, {
885 operator: node.operator.slice(0, -1),
886 left: make_ref(left, fixed),
887 right: node.right,
888 });
889 };
890 left.fixed.assigns = !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
891 left.fixed.assigns.push(node);
892 } else {
893 left.walk(tw);
894 d.fixed = false;
895 }
896 return true;
897 }
898
899 function walk_prop(lhs) {
900 if (lhs instanceof AST_Dot) {
901 walk_prop(lhs.expression);
902 } else if (lhs instanceof AST_Sub) {
903 walk_prop(lhs.expression);
904 lhs.property.walk(tw);
905 } else if (lhs instanceof AST_SymbolRef) {
906 var d = lhs.definition();
907 push_ref(d, lhs);
908 if (d.fixed) {
909 lhs.fixed = d.fixed;
910 if (lhs.fixed.assigns) {
911 lhs.fixed.assigns.push(node);
912 } else {
913 lhs.fixed.assigns = [ node ];
914 }
915 }
916 } else {
917 lhs.walk(tw);
918 }
919 }
920
921 function walk_assign() {
922 right.walk(tw);
923 var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive_ref(tw, d));
924 scan_declaration(tw, compressor, left, function() {
925 return node.right;
926 }, function(sym, fixed, walk) {
927 if (!(sym instanceof AST_SymbolRef)) {
928 mark_assignment_to_arguments(sym);
929 walk();
930 return;
931 }
932 var d = sym.definition();
933 d.assignments++;
934 if (fixed && !modified && !sym.in_arg && safe_to_assign(tw, d)) {
935 push_ref(d, sym);
936 mark(tw, d);
937 if (d.single_use && left instanceof AST_Destructured) d.single_use = false;
938 tw.loop_ids[d.id] = tw.in_loop;
939 mark_escaped(tw, d, sym.scope, node, right, 0, 1);
940 sym.fixed = d.fixed = fixed;
941 sym.fixed.assigns = [ node ];
942 } else {
943 walk();
944 d.fixed = false;
945 }
946 });
947 }
948 });
949 def(AST_Binary, function(tw) {
950 if (!lazy_op[this.operator]) return;
951 this.left.walk(tw);
952 push(tw);
953 this.right.walk(tw);
954 pop(tw);
955 return true;
956 });
957 def(AST_BlockScope, function(tw, descend, compressor) {
958 this.variables.each(function(def) {
959 reset_def(tw, compressor, def);
960 });
961 });
962 def(AST_Call, function(tw, descend) {
963 tw.find_parent(AST_Scope).may_call_this();
964 var exp = this.expression;
965 if (exp instanceof AST_LambdaExpression) {
966 var iife = is_iife_single(this);
967 this.args.forEach(function(arg) {
968 arg.walk(tw);
969 if (arg instanceof AST_Spread) iife = false;
970 });
971 if (iife) exp.reduce_vars = reduce_iife;
972 exp.walk(tw);
973 if (iife) delete exp.reduce_vars;
974 return true;
975 }
976 if (exp instanceof AST_SymbolRef) {
977 var def = exp.definition();
978 if (this.TYPE == "Call" && tw.in_boolean_context()) def.bool_fn++;
979 if (def.fixed instanceof AST_LambdaDefinition) {
980 var defun = mark_defun(tw, def);
981 if (defun) {
982 descend();
983 defun.walk(tw);
984 return true;
985 }
986 }
987 } else if (this.TYPE == "Call"
988 && exp instanceof AST_Assign
989 && exp.operator == "="
990 && exp.left instanceof AST_SymbolRef
991 && tw.in_boolean_context()) {
992 exp.left.definition().bool_fn++;
993 }
994 if (!this.optional) return;
995 exp.walk(tw);
996 push(tw);
997 this.args.forEach(function(arg) {
998 arg.walk(tw);
999 });
1000 pop(tw);
1001 return true;
1002 });
1003 def(AST_Class, function(tw, descend, compressor) {
1004 var node = this;
1005 node.variables.each(function(def) {
1006 reset_def(tw, compressor, def);
1007 });
1008 if (node.extends) node.extends.walk(tw);
1009 if (node.name) {
1010 var d = node.name.definition();
1011 var parent = tw.parent();
1012 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false;
1013 if (safe_to_assign(tw, d, true)) {
1014 mark(tw, d);
1015 tw.loop_ids[d.id] = tw.in_loop;
1016 d.fixed = function() {
1017 return node;
1018 };
1019 d.fixed.assigns = [ node ];
1020 if (!is_safe_lexical(d)) d.single_use = false;
1021 } else {
1022 d.fixed = false;
1023 }
1024 }
1025 node.properties.filter(function(prop) {
1026 reset_flags(prop);
1027 if (prop.key instanceof AST_Node) prop.key.walk(tw);
1028 return prop.value;
1029 }).forEach(function(prop) {
1030 if (prop.static && (prop.value instanceof AST_Lambda || !prop.value.contains_this())) {
1031 prop.value.walk(tw);
1032 } else {
1033 push(tw);
1034 prop.value.walk(tw);
1035 pop(tw);
1036 }
1037 });
1038 return true;
1039 });
1040 def(AST_Conditional, function(tw) {
1041 this.condition.walk(tw);
1042 push(tw);
1043 this.consequent.walk(tw);
1044 pop(tw);
1045 push(tw);
1046 this.alternative.walk(tw);
1047 pop(tw);
1048 return true;
1049 });
1050 def(AST_DefaultValue, function(tw) {
1051 this.name.walk(tw);
1052 push(tw);
1053 this.value.walk(tw);
1054 pop(tw);
1055 return true;
1056 });
1057 def(AST_Do, function(tw) {
1058 var saved_loop = tw.in_loop;
1059 tw.in_loop = this;
1060 push(tw);
1061 this.body.walk(tw);
1062 if (has_loop_control(this, tw.parent())) {
1063 pop(tw);
1064 push(tw);
1065 }
1066 this.condition.walk(tw);
1067 pop(tw);
1068 tw.in_loop = saved_loop;
1069 return true;
1070 });
1071 def(AST_For, function(tw, descend, compressor) {
1072 this.variables.each(function(def) {
1073 reset_def(tw, compressor, def);
1074 });
1075 if (this.init) this.init.walk(tw);
1076 var saved_loop = tw.in_loop;
1077 tw.in_loop = this;
1078 push(tw);
1079 if (this.condition) this.condition.walk(tw);
1080 this.body.walk(tw);
1081 if (this.step) {
1082 if (has_loop_control(this, tw.parent())) {
1083 pop(tw);
1084 push(tw);
1085 }
1086 this.step.walk(tw);
1087 }
1088 pop(tw);
1089 tw.in_loop = saved_loop;
1090 return true;
1091 });
1092 def(AST_ForEnumeration, function(tw, descend, compressor) {
1093 this.variables.each(function(def) {
1094 reset_def(tw, compressor, def);
1095 });
1096 this.object.walk(tw);
1097 var saved_loop = tw.in_loop;
1098 tw.in_loop = this;
1099 push(tw);
1100 var init = this.init;
1101 if (init instanceof AST_Definitions) {
1102 init.definitions[0].name.mark_symbol(function(node) {
1103 if (node instanceof AST_SymbolDeclaration) {
1104 var def = node.definition();
1105 def.assignments++;
1106 def.fixed = false;
1107 }
1108 }, tw);
1109 } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) {
1110 init.mark_symbol(function(node) {
1111 if (node instanceof AST_SymbolRef) {
1112 var def = node.definition();
1113 push_ref(def, node);
1114 def.assignments++;
1115 if (!node.is_immutable()) def.fixed = false;
1116 }
1117 }, tw);
1118 } else {
1119 init.walk(tw);
1120 }
1121 this.body.walk(tw);
1122 pop(tw);
1123 tw.in_loop = saved_loop;
1124 return true;
1125 });
1126 def(AST_If, function(tw) {
1127 this.condition.walk(tw);
1128 push(tw);
1129 this.body.walk(tw);
1130 pop(tw);
1131 if (this.alternative) {
1132 push(tw);
1133 this.alternative.walk(tw);
1134 pop(tw);
1135 }
1136 return true;
1137 });
1138 def(AST_LabeledStatement, function(tw) {
1139 push(tw);
1140 this.body.walk(tw);
1141 pop(tw);
1142 return true;
1143 });
1144 def(AST_Lambda, function(tw, descend, compressor) {
1145 var fn = this;
1146 fn.inlined = false;
1147 push(tw);
1148 reset_variables(tw, compressor, fn);
1149 descend();
1150 pop(tw);
1151 if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
1152 walk_defuns(tw, fn);
1153 return true;
1154 });
1155 def(AST_LambdaDefinition, function(tw, descend, compressor) {
1156 var fn = this;
1157 var def = fn.name.definition();
1158 var parent = tw.parent();
1159 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false;
1160 if (tw.defun_visited[def.id]) return true;
1161 if (def.init === fn && tw.defun_ids[def.id] !== tw.safe_ids) return true;
1162 tw.defun_visited[def.id] = true;
1163 fn.inlined = false;
1164 push(tw);
1165 reset_variables(tw, compressor, fn);
1166 descend();
1167 pop(tw);
1168 walk_defuns(tw, fn);
1169 return true;
1170 });
1171 def(AST_Sub, function(tw) {
1172 if (!this.optional) return;
1173 this.expression.walk(tw);
1174 push(tw);
1175 this.property.walk(tw);
1176 pop(tw);
1177 return true;
1178 });
1179 def(AST_Switch, function(tw, descend, compressor) {
1180 this.variables.each(function(def) {
1181 reset_def(tw, compressor, def);
1182 });
1183 this.expression.walk(tw);
1184 var first = true;
1185 this.body.forEach(function(branch) {
1186 if (branch instanceof AST_Default) return;
1187 branch.expression.walk(tw);
1188 if (first) {
1189 first = false;
1190 push(tw);
1191 }
1192 })
1193 if (!first) pop(tw);
1194 walk_body(this, tw);
1195 return true;
1196 });
1197 def(AST_SwitchBranch, function(tw) {
1198 push(tw);
1199 walk_body(this, tw);
1200 pop(tw);
1201 return true;
1202 });
1203 def(AST_SymbolCatch, function() {
1204 this.definition().fixed = false;
1205 });
1206 def(AST_SymbolImport, function() {
1207 this.definition().fixed = false;
1208 });
1209 def(AST_SymbolRef, function(tw, descend, compressor) {
1210 var d = this.definition();
1211 push_ref(d, this);
1212 if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) {
1213 tw.loop_ids[d.id] = tw.in_loop;
1214 }
1215 var recursive = recursive_ref(tw, d);
1216 if (recursive) recursive.enclosed.forEach(function(def) {
1217 if (d === def) return;
1218 if (def.scope === recursive) return;
1219 var assigns = def.fixed && def.fixed.assigns;
1220 if (!assigns) return;
1221 if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
1222 var safe = tw.safe_ids[def.id];
1223 if (!safe) return;
1224 safe.assign = true;
1225 });
1226 if (d.fixed === false) {
1227 var redef = d.redefined();
1228 if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false;
1229 } else if (d.fixed === undefined || !safe_to_read(tw, d)) {
1230 d.fixed = false;
1231 } else if (d.fixed) {
1232 if (this.in_arg && d.orig[0] instanceof AST_SymbolLambda) this.fixed = d.scope;
1233 var value = this.fixed_value();
1234 if (recursive) {
1235 d.recursive_refs++;
1236 } else if (value && ref_once(compressor, d)) {
1237 d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
1238 d.single_use = is_lambda(value)
1239 && !value.pinned()
1240 && (!d.in_loop || tw.parent() instanceof AST_Call)
1241 || !d.in_loop
1242 && d.scope === this.scope.resolve()
1243 && value.is_constant_expression();
1244 } else {
1245 d.single_use = false;
1246 }
1247 if (is_modified(compressor, tw, this, value, 0, is_immutable(value), recursive)) {
1248 if (d.single_use) {
1249 d.single_use = "m";
1250 } else {
1251 d.fixed = false;
1252 }
1253 }
1254 if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true;
1255 mark_escaped(tw, d, this.scope, this, value, 0, 1);
1256 }
1257 if (!this.fixed) this.fixed = d.fixed;
1258 var parent;
1259 if (d.fixed instanceof AST_LambdaDefinition
1260 && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
1261 var defun = mark_defun(tw, d);
1262 if (defun) defun.walk(tw);
1263 }
1264 });
1265 def(AST_Toplevel, function(tw, descend, compressor) {
1266 this.globals.each(function(def) {
1267 reset_def(tw, compressor, def);
1268 });
1269 push(tw);
1270 reset_variables(tw, compressor, this);
1271 descend();
1272 pop(tw);
1273 walk_defuns(tw, this);
1274 return true;
1275 });
1276 def(AST_Try, function(tw, descend, compressor) {
1277 this.variables.each(function(def) {
1278 reset_def(tw, compressor, def);
1279 });
1280 push(tw);
1281 walk_body(this, tw);
1282 pop(tw);
1283 if (this.bcatch) {
1284 push(tw);
1285 this.bcatch.walk(tw);
1286 pop(tw);
1287 }
1288 if (this.bfinally) this.bfinally.walk(tw);
1289 return true;
1290 });
1291 def(AST_Unary, function(tw, descend) {
1292 var node = this;
1293 if (!UNARY_POSTFIX[node.operator]) return;
1294 var exp = node.expression;
1295 if (!(exp instanceof AST_SymbolRef)) {
1296 mark_assignment_to_arguments(exp);
1297 return;
1298 }
1299 var d = exp.definition();
1300 d.assignments++;
1301 var fixed = d.fixed;
1302 if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) {
1303 push_ref(d, exp);
1304 mark(tw, d);
1305 if (d.single_use) d.single_use = false;
1306 d.fixed = function() {
1307 return make_node(AST_Binary, node, {
1308 operator: node.operator.slice(0, -1),
1309 left: make_node(AST_UnaryPrefix, node, {
1310 operator: "+",
1311 expression: make_ref(exp, fixed)
1312 }),
1313 right: make_node(AST_Number, node, {
1314 value: 1
1315 })
1316 });
1317 };
1318 d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
1319 d.fixed.assigns.push(node);
1320 if (node instanceof AST_UnaryPrefix) {
1321 exp.fixed = d.fixed;
1322 } else {
1323 exp.fixed = function() {
1324 return make_node(AST_UnaryPrefix, node, {
1325 operator: "+",
1326 expression: make_ref(exp, fixed)
1327 });
1328 };
1329 exp.fixed.assigns = fixed && fixed.assigns;
1330 }
1331 } else {
1332 exp.walk(tw);
1333 d.fixed = false;
1334 }
1335 return true;
1336 });
1337 def(AST_VarDef, function(tw, descend, compressor) {
1338 var node = this;
1339 if (node.value) {
1340 node.value.walk(tw);
1341 } else if (!(tw.parent() instanceof AST_Let)) {
1342 return;
1343 }
1344 scan_declaration(tw, compressor, node.name, function() {
1345 return node.value || make_node(AST_Undefined, node);
1346 }, function(name, fixed) {
1347 var d = name.definition();
1348 if (fixed && safe_to_assign(tw, d, true)) {
1349 mark(tw, d);
1350 tw.loop_ids[d.id] = tw.in_loop;
1351 d.fixed = fixed;
1352 d.fixed.assigns = [ node ];
1353 if (name instanceof AST_SymbolConst && d.redefined()
1354 || !(can_drop_symbol(name) || is_safe_lexical(d))) {
1355 d.single_use = false;
1356 }
1357 } else {
1358 d.fixed = false;
1359 }
1360 });
1361 return true;
1362 });
1363 def(AST_While, function(tw, descend) {
1364 var saved_loop = tw.in_loop;
1365 tw.in_loop = this;
1366 push(tw);
1367 descend();
1368 pop(tw);
1369 tw.in_loop = saved_loop;
1370 return true;
1371 });
1372 })(function(node, func) {
1373 node.DEFMETHOD("reduce_vars", func);
1374 });
1375
1376 function reset_flags(node) {
1377 node._squeezed = false;
1378 node._optimized = false;
1379 delete node.fixed;
1380 if (node instanceof AST_Scope) delete node._var_names;
1381 }
1382
1383 AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
1384 var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
1385 reset_flags(node);
1386 return node.reduce_vars(tw, descend, compressor);
1387 } : reset_flags);
1388 // Flow control for visiting `AST_Defun`s
1389 tw.defun_ids = Object.create(null);
1390 tw.defun_visited = Object.create(null);
1391 // Record the loop body in which `AST_SymbolDeclaration` is first encountered
1392 tw.in_loop = null;
1393 tw.loop_ids = Object.create(null);
1394 // Stack of look-up tables to keep track of whether a `SymbolDef` has been
1395 // properly assigned before use:
1396 // - `push()` & `pop()` when visiting conditional branches
1397 // - backup & restore via `save_ids` when visiting out-of-order sections
1398 tw.safe_ids = Object.create(null);
1399 this.walk(tw);
1400 });
1401
1402 AST_Symbol.DEFMETHOD("fixed_value", function() {
1403 var fixed = this.definition().fixed;
1404 if (!fixed) return fixed;
1405 if (this.fixed) fixed = this.fixed;
1406 return fixed instanceof AST_Node ? fixed : fixed();
1407 });
1408
1409 AST_SymbolRef.DEFMETHOD("is_immutable", function() {
1410 var def = this.redef || this.definition();
1411 return def.orig.length == 1 && def.orig[0] instanceof AST_SymbolLambda;
1412 });
1413
1414 AST_Node.DEFMETHOD("convert_symbol", noop);
1415 function convert_destructured(type, process) {
1416 return this.transform(new TreeTransformer(function(node, descend) {
1417 if (node instanceof AST_DefaultValue) {
1418 node = node.clone();
1419 node.name = node.name.transform(this);
1420 return node;
1421 }
1422 if (node instanceof AST_Destructured) {
1423 node = node.clone();
1424 descend(node, this);
1425 return node;
1426 }
1427 if (node instanceof AST_DestructuredKeyVal) {
1428 node = node.clone();
1429 node.value = node.value.transform(this);
1430 return node;
1431 }
1432 return node.convert_symbol(type, process);
1433 }));
1434 }
1435 AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured);
1436 AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
1437 function convert_symbol(type, process) {
1438 var node = make_node(type, this, this);
1439 process(node, this);
1440 return node;
1441 }
1442 AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
1443 AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
1444
1445 function mark_destructured(process, tw) {
1446 var marker = new TreeWalker(function(node) {
1447 if (node instanceof AST_DefaultValue) {
1448 node.value.walk(tw);
1449 node.name.walk(marker);
1450 return true;
1451 }
1452 if (node instanceof AST_DestructuredKeyVal) {
1453 if (node.key instanceof AST_Node) node.key.walk(tw);
1454 node.value.walk(marker);
1455 return true;
1456 }
1457 return process(node);
1458 });
1459 this.walk(marker);
1460 }
1461 AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured);
1462 AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured);
1463 function mark_symbol(process) {
1464 return process(this);
1465 }
1466 AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol);
1467 AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol);
1468
1469 AST_Node.DEFMETHOD("match_symbol", function(predicate) {
1470 return predicate(this);
1471 });
1472 AST_Destructured.DEFMETHOD("match_symbol", function(predicate, ignore_side_effects) {
1473 var found = false;
1474 var tw = new TreeWalker(function(node) {
1475 if (found) return true;
1476 if (node instanceof AST_DefaultValue) {
1477 if (!ignore_side_effects) return found = true;
1478 node.name.walk(tw);
1479 return true;
1480 }
1481 if (node instanceof AST_DestructuredKeyVal) {
1482 if (!ignore_side_effects && node.key instanceof AST_Node) return found = true;
1483 node.value.walk(tw);
1484 return true;
1485 }
1486 if (predicate(node)) return found = true;
1487 });
1488 this.walk(tw);
1489 return found;
1490 });
1491
1492 function in_async_generator(scope) {
1493 return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction;
1494 }
1495
1496 function find_scope(compressor) {
1497 var level = 0, node;
1498 while (node = compressor.parent(level++)) {
1499 if (node.variables) return node;
1500 }
1501 }
1502
1503 var identifier_atom = makePredicate("Infinity NaN undefined");
1504 function is_lhs_read_only(lhs, compressor) {
1505 if (lhs instanceof AST_ObjectIdentity) return true;
1506 if (lhs instanceof AST_PropAccess) {
1507 if (lhs.property == "__proto__") return true;
1508 lhs = lhs.expression;
1509 if (lhs instanceof AST_SymbolRef) {
1510 if (lhs.is_immutable()) return false;
1511 lhs = lhs.fixed_value();
1512 }
1513 if (!lhs) return true;
1514 if (lhs.tail_node().is_constant()) return true;
1515 return is_lhs_read_only(lhs, compressor);
1516 }
1517 if (lhs instanceof AST_SymbolRef) {
1518 if (lhs.is_immutable()) return true;
1519 var def = lhs.definition();
1520 return compressor.exposed(def) && identifier_atom[def.name];
1521 }
1522 return false;
1523 }
1524
1525 function make_node(ctor, orig, props) {
1526 if (!props) props = {};
1527 if (orig) {
1528 if (!props.start) props.start = orig.start;
1529 if (!props.end) props.end = orig.end;
1530 }
1531 return new ctor(props);
1532 }
1533
1534 function make_sequence(orig, expressions) {
1535 if (expressions.length == 1) return expressions[0];
1536 return make_node(AST_Sequence, orig, {
1537 expressions: expressions.reduce(merge_sequence, [])
1538 });
1539 }
1540
1541 function make_node_from_constant(val, orig) {
1542 switch (typeof val) {
1543 case "string":
1544 return make_node(AST_String, orig, {
1545 value: val
1546 });
1547 case "number":
1548 if (isNaN(val)) return make_node(AST_NaN, orig);
1549 if (isFinite(val)) {
1550 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
1551 operator: "-",
1552 expression: make_node(AST_Number, orig, { value: -val })
1553 }) : make_node(AST_Number, orig, { value: val });
1554 }
1555 return val < 0 ? make_node(AST_UnaryPrefix, orig, {
1556 operator: "-",
1557 expression: make_node(AST_Infinity, orig)
1558 }) : make_node(AST_Infinity, orig);
1559 case "boolean":
1560 return make_node(val ? AST_True : AST_False, orig);
1561 case "undefined":
1562 return make_node(AST_Undefined, orig);
1563 default:
1564 if (val === null) {
1565 return make_node(AST_Null, orig, { value: null });
1566 }
1567 if (val instanceof RegExp) {
1568 return make_node(AST_RegExp, orig, { value: val });
1569 }
1570 throw new Error(string_template("Can't handle constant of type: {type}", {
1571 type: typeof val
1572 }));
1573 }
1574 }
1575
1576 function needs_unbinding(compressor, val) {
1577 return val instanceof AST_PropAccess
1578 || is_undeclared_ref(val) && val.name == "eval";
1579 }
1580
1581 // we shouldn't compress (1,func)(something) to
1582 // func(something) because that changes the meaning of
1583 // the func (becomes lexical instead of global).
1584 function maintain_this_binding(compressor, parent, orig, val) {
1585 var wrap = false;
1586 if (parent.TYPE == "Call") {
1587 wrap = parent.expression === orig && needs_unbinding(compressor, val);
1588 } else if (parent instanceof AST_Template) {
1589 wrap = parent.tag === orig && needs_unbinding(compressor, val);
1590 } else if (parent instanceof AST_UnaryPrefix) {
1591 wrap = parent.operator == "delete"
1592 || parent.operator == "typeof" && is_undeclared_ref(val);
1593 }
1594 return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
1595 }
1596
1597 function merge_sequence(array, node) {
1598 if (node instanceof AST_Sequence) {
1599 array.push.apply(array, node.expressions);
1600 } else {
1601 array.push(node);
1602 }
1603 return array;
1604 }
1605
1606 function is_lexical_definition(stat) {
1607 return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let;
1608 }
1609
1610 function safe_to_trim(stat) {
1611 if (stat instanceof AST_LambdaDefinition) {
1612 var def = stat.name.definition();
1613 return def.scope === stat.name.scope || all(def.references, function(ref) {
1614 var scope = ref.scope;
1615 do {
1616 if (scope === stat.name.scope) return true;
1617 } while (scope = scope.parent_scope);
1618 });
1619 }
1620 return !is_lexical_definition(stat);
1621 }
1622
1623 function as_statement_array(thing) {
1624 if (thing === null) return [];
1625 if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ];
1626 if (thing instanceof AST_EmptyStatement) return [];
1627 if (is_statement(thing)) return [ thing ];
1628 throw new Error("Can't convert thing to statement array");
1629 }
1630
1631 function is_empty(thing) {
1632 if (thing === null) return true;
1633 if (thing instanceof AST_EmptyStatement) return true;
1634 if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
1635 return false;
1636 }
1637
1638 function has_declarations_only(block) {
1639 return all(block.body, function(stat) {
1640 return is_empty(stat)
1641 || stat instanceof AST_Defun
1642 || stat instanceof AST_Var && all(stat.definitions, function(var_def) {
1643 return !var_def.value;
1644 });
1645 });
1646 }
1647
1648 function loop_body(x) {
1649 if (x instanceof AST_IterationStatement) {
1650 return x.body instanceof AST_BlockStatement ? x.body : x;
1651 }
1652 return x;
1653 }
1654
1655 function root_expr(prop) {
1656 while (prop instanceof AST_PropAccess) prop = prop.expression;
1657 return prop;
1658 }
1659
1660 function is_iife_call(node) {
1661 if (node.TYPE != "Call") return false;
1662 do {
1663 node = node.expression;
1664 } while (node instanceof AST_PropAccess);
1665 return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
1666 }
1667
1668 function is_iife_single(call) {
1669 var exp = call.expression;
1670 if (exp.name) return false;
1671 if (!(call instanceof AST_New)) return true;
1672 var found = false;
1673 exp.walk(new TreeWalker(function(node) {
1674 if (found) return true;
1675 if (node instanceof AST_NewTarget) return found = true;
1676 if (node instanceof AST_Scope && node !== exp) return true;
1677 }));
1678 return !found;
1679 }
1680
1681 function is_undeclared_ref(node) {
1682 return node instanceof AST_SymbolRef && node.definition().undeclared;
1683 }
1684
1685 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");
1686 AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
1687 return this.defined
1688 || !this.definition().undeclared
1689 || compressor.option("unsafe") && global_names[this.name];
1690 });
1691
1692 function declarations_only(node) {
1693 return all(node.definitions, function(var_def) {
1694 return !var_def.value;
1695 });
1696 }
1697
1698 function is_declaration(stat) {
1699 return stat instanceof AST_Defun || stat instanceof AST_Var && declarations_only(stat);
1700 }
1701
1702 function tighten_body(statements, compressor) {
1703 var in_loop, in_try, scope;
1704 find_loop_scope_try();
1705 var CHANGED, max_iter = 10;
1706 do {
1707 CHANGED = false;
1708 eliminate_spurious_blocks(statements);
1709 if (compressor.option("dead_code")) {
1710 eliminate_dead_code(statements, compressor);
1711 }
1712 if (compressor.option("if_return")) {
1713 handle_if_return(statements, compressor);
1714 }
1715 if (compressor.sequences_limit > 0) {
1716 sequencesize(statements, compressor);
1717 sequencesize_2(statements, compressor);
1718 }
1719 if (compressor.option("join_vars")) {
1720 join_consecutive_vars(statements);
1721 }
1722 if (compressor.option("collapse_vars")) {
1723 collapse(statements, compressor);
1724 }
1725 } while (CHANGED && max_iter-- > 0);
1726 return statements;
1727
1728 function find_loop_scope_try() {
1729 var node = compressor.self(), level = 0;
1730 do {
1731 if (node instanceof AST_Catch) {
1732 if (!compressor.parent(level).bfinally) level++;
1733 } else if (node instanceof AST_Finally) {
1734 level++;
1735 } else if (node instanceof AST_IterationStatement) {
1736 in_loop = true;
1737 } else if (node instanceof AST_Scope) {
1738 scope = node;
1739 break;
1740 } else if (node instanceof AST_Try) {
1741 if (!in_try) in_try = node;
1742 }
1743 } while (node = compressor.parent(level++));
1744 }
1745
1746 // Search from right to left for assignment-like expressions:
1747 // - `var a = x;`
1748 // - `a = x;`
1749 // - `++a`
1750 // For each candidate, scan from left to right for first usage, then try
1751 // to fold assignment into the site for compression.
1752 // Will not attempt to collapse assignments into or past code blocks
1753 // which are not sequentially executed, e.g. loops and conditionals.
1754 function collapse(statements, compressor) {
1755 if (scope.pinned()) return statements;
1756 var args;
1757 var assignments = Object.create(null);
1758 var candidates = [];
1759 var declare_only = Object.create(null);
1760 var force_single;
1761 var stat_index = statements.length;
1762 var scanner = new TreeTransformer(function(node, descend) {
1763 if (abort) return node;
1764 // Skip nodes before `candidate` as quickly as possible
1765 if (!hit) {
1766 if (node !== hit_stack[hit_index]) return node;
1767 hit_index++;
1768 if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner);
1769 hit = true;
1770 stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
1771 if (stop_after === node) abort = true;
1772 return node;
1773 }
1774 // Stop immediately if these node types are encountered
1775 var parent = scanner.parent();
1776 if (should_stop(node, parent)) {
1777 abort = true;
1778 return node;
1779 }
1780 // Stop only if candidate is found within conditional branches
1781 if (!stop_if_hit && in_conditional(node, parent)) {
1782 stop_if_hit = parent;
1783 }
1784 // Skip transient nodes caused by single-use variable replacement
1785 if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
1786 // Replace variable with assignment when found
1787 var hit_rhs;
1788 if (!(node instanceof AST_SymbolDeclaration)
1789 && (scan_lhs && lhs.equivalent_to(node)
1790 || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
1791 if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
1792 if (!hit_rhs && !value_def) abort = true;
1793 return node;
1794 }
1795 if (is_lhs(node, parent)) {
1796 if (value_def && !hit_rhs) {
1797 assign_used = true;
1798 replaced++;
1799 }
1800 return node;
1801 } else if (value_def) {
1802 if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced;
1803 if (!hit_rhs) replaced++;
1804 return node;
1805 } else {
1806 replaced++;
1807 }
1808 CHANGED = abort = true;
1809 AST_Node.info("Collapsing {node} [{file}:{line},{col}]", {
1810 node: node,
1811 file: node.start.file,
1812 line: node.start.line,
1813 col: node.start.col,
1814 });
1815 if (candidate.TYPE == "Binary") return make_node(AST_Assign, candidate, {
1816 operator: "=",
1817 left: candidate.right.left,
1818 right: make_node(AST_Conditional, candidate, {
1819 condition: candidate.operator == "&&" ? candidate.left : candidate.left.negate(compressor),
1820 consequent: candidate.right.right,
1821 alternative: node,
1822 }),
1823 });
1824 if (candidate instanceof AST_UnaryPostfix) {
1825 if (lhs instanceof AST_SymbolRef) lhs.definition().fixed = false;
1826 return make_node(AST_UnaryPrefix, candidate, candidate);
1827 }
1828 if (candidate instanceof AST_VarDef) {
1829 var def = candidate.name.definition();
1830 if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
1831 def.replaced++;
1832 return maintain_this_binding(compressor, parent, node, candidate.value);
1833 }
1834 return make_node(AST_Assign, candidate, {
1835 operator: "=",
1836 left: make_node(AST_SymbolRef, candidate.name, candidate.name),
1837 right: candidate.value,
1838 });
1839 }
1840 var assign = candidate;
1841 while (assign.write_only) {
1842 assign.write_only = false;
1843 if (!(assign instanceof AST_Assign)) break;
1844 assign = assign.right;
1845 }
1846 return candidate;
1847 }
1848 // These node types have child nodes that execute sequentially,
1849 // but are otherwise not safe to scan into or beyond them.
1850 if (is_last_node(node, parent) || may_throw(node)) {
1851 stop_after = node;
1852 if (node instanceof AST_Scope) abort = true;
1853 }
1854 // Scan but don't replace inside getter/setter
1855 if (node instanceof AST_Accessor) {
1856 var replace = can_replace;
1857 can_replace = false;
1858 descend(node, scanner);
1859 can_replace = replace;
1860 return signal_abort(node);
1861 }
1862 // Scan but don't replace inside destructuring expression
1863 if (node instanceof AST_Destructured) {
1864 var replace = can_replace;
1865 can_replace = false;
1866 descend(node, scanner);
1867 can_replace = replace;
1868 return signal_abort(node);
1869 }
1870 // Scan but don't replace inside default value
1871 if (node instanceof AST_DefaultValue) {
1872 node.name = node.name.transform(scanner);
1873 var replace = can_replace;
1874 can_replace = false;
1875 node.value = node.value.transform(scanner);
1876 can_replace = replace;
1877 return signal_abort(node);
1878 }
1879 // Scan but don't replace inside block scope with colliding variable
1880 if (node instanceof AST_BlockScope
1881 && !(node instanceof AST_Scope)
1882 && !(node.variables && node.variables.all(function(def) {
1883 return !lvalues.has(def.name);
1884 }))) {
1885 var replace = can_replace;
1886 can_replace = false;
1887 if (!handle_custom_scan_order(node, scanner)) descend(node, scanner);
1888 can_replace = replace;
1889 return signal_abort(node);
1890 }
1891 return handle_custom_scan_order(node, scanner);
1892 }, signal_abort);
1893 var multi_replacer = new TreeTransformer(function(node) {
1894 if (abort) return node;
1895 // Skip nodes before `candidate` as quickly as possible
1896 if (!hit) {
1897 if (node !== hit_stack[hit_index]) return node;
1898 hit_index++;
1899 switch (hit_stack.length - hit_index) {
1900 case 0:
1901 hit = true;
1902 if (assign_used) return node;
1903 if (node !== candidate) return node;
1904 if (node instanceof AST_VarDef) return node;
1905 def.replaced++;
1906 var parent = multi_replacer.parent();
1907 if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
1908 value_def.replaced++;
1909 return List.skip;
1910 }
1911 return rvalue;
1912 case 1:
1913 if (!assign_used && node.body === candidate) {
1914 hit = true;
1915 def.replaced++;
1916 value_def.replaced++;
1917 return null;
1918 }
1919 default:
1920 return handle_custom_scan_order(node, multi_replacer);
1921 }
1922 }
1923 // Replace variable when found
1924 if (node instanceof AST_SymbolRef
1925 && node.name == def.name) {
1926 if (!--replaced) abort = true;
1927 if (is_lhs(node, multi_replacer.parent())) return node;
1928 var ref = rvalue.clone();
1929 ref.scope = node.scope;
1930 ref.reference();
1931 if (replaced == assign_pos) {
1932 abort = true;
1933 return make_node(AST_Assign, candidate, {
1934 operator: "=",
1935 left: node,
1936 right: ref,
1937 });
1938 }
1939 def.replaced++;
1940 return ref;
1941 }
1942 // Skip (non-executed) functions and (leading) default case in switch statements
1943 if (node instanceof AST_Default || node instanceof AST_Scope) return node;
1944 }, patch_sequence);
1945 while (--stat_index >= 0) {
1946 // Treat parameters as collapsible in IIFE, i.e.
1947 // function(a, b){ ... }(x());
1948 // would be translated into equivalent assignments:
1949 // var a = x(), b = undefined;
1950 if (stat_index == 0 && compressor.option("unused")) extract_args();
1951 // Find collapsible assignments
1952 var hit_stack = [];
1953 extract_candidates(statements[stat_index]);
1954 while (candidates.length > 0) {
1955 hit_stack = candidates.pop();
1956 var hit_index = 0;
1957 var candidate = hit_stack[hit_stack.length - 1];
1958 var assign_pos = -1;
1959 var assign_used = false;
1960 var remaining;
1961 var value_def = null;
1962 var stop_after = null;
1963 var stop_if_hit = null;
1964 var lhs = get_lhs(candidate);
1965 var side_effects = lhs && lhs.has_side_effects(compressor);
1966 var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
1967 var scan_rhs = foldable(candidate);
1968 if (!scan_lhs && !scan_rhs) continue;
1969 var funarg = candidate.name instanceof AST_SymbolFunarg;
1970 var may_throw = return_false;
1971 if (candidate.may_throw(compressor)) {
1972 if (funarg && is_async(scope)) continue;
1973 may_throw = in_try ? function(node) {
1974 return node.has_side_effects(compressor);
1975 } : side_effects_external;
1976 }
1977 var read_toplevel = false;
1978 var modify_toplevel = false;
1979 // Locate symbols which may execute code outside of scanning range
1980 var lvalues = get_lvalues(candidate);
1981 var lhs_local = is_lhs_local(lhs);
1982 var rvalue = get_rvalue(candidate);
1983 if (!side_effects) side_effects = value_has_side_effects();
1984 var check_destructured = in_try || !lhs_local ? function(node) {
1985 return node instanceof AST_Destructured;
1986 } : return_false;
1987 var replace_all = replace_all_symbols(candidate);
1988 var hit = funarg;
1989 var abort = false;
1990 var replaced = 0;
1991 var can_replace = !args || !hit;
1992 if (!can_replace) {
1993 for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) {
1994 if (args[j]) args[j].transform(scanner);
1995 }
1996 can_replace = true;
1997 }
1998 for (var i = stat_index; !abort && i < statements.length; i++) {
1999 statements[i].transform(scanner);
2000 }
2001 if (value_def) {
2002 if (!replaced || remaining > replaced) {
2003 candidates.push(hit_stack);
2004 force_single = true;
2005 continue;
2006 }
2007 if (replaced == assign_pos) assign_used = true;
2008 var def = lhs.definition();
2009 abort = false;
2010 hit_index = 0;
2011 hit = funarg;
2012 for (var i = stat_index; !abort && i < statements.length; i++) {
2013 if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
2014 }
2015 replaced = candidate instanceof AST_VarDef
2016 && candidate === hit_stack[hit_stack.length - 1]
2017 && def.references.length == def.replaced
2018 && !compressor.exposed(def);
2019 value_def.last_ref = false;
2020 value_def.single_use = false;
2021 CHANGED = true;
2022 }
2023 if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
2024 }
2025 }
2026
2027 function signal_abort(node) {
2028 if (abort) return node;
2029 if (stop_after === node) abort = true;
2030 if (stop_if_hit === node) stop_if_hit = null;
2031 return node;
2032 }
2033
2034 function handle_custom_scan_order(node, tt) {
2035 if (!(node instanceof AST_BlockScope)) {
2036 if (!(node instanceof AST_ClassProperty && !node.static)) return;
2037 // Skip non-static class property values
2038 if (node.key instanceof AST_Node) node.key = node.key.transform(tt);
2039 return node;
2040 }
2041 // Skip (non-executed) functions
2042 if (node instanceof AST_Scope) return node;
2043 // Scan object only in a for-in/of statement
2044 if (node instanceof AST_ForEnumeration) {
2045 node.object = node.object.transform(tt);
2046 abort = true;
2047 return node;
2048 }
2049 // Scan first case expression only in a switch statement
2050 if (node instanceof AST_Switch) {
2051 node.expression = node.expression.transform(tt);
2052 for (var i = 0; !abort && i < node.body.length; i++) {
2053 var branch = node.body[i];
2054 if (branch instanceof AST_Case) {
2055 if (!hit) {
2056 if (branch !== hit_stack[hit_index]) continue;
2057 hit_index++;
2058 }
2059 branch.expression = branch.expression.transform(tt);
2060 if (!replace_all) break;
2061 scan_rhs = false;
2062 }
2063 }
2064 abort = true;
2065 return node;
2066 }
2067 }
2068
2069 function is_direct_assignment(node, parent) {
2070 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node;
2071 if (parent instanceof AST_DefaultValue) return parent.name === node;
2072 if (parent instanceof AST_DestructuredArray) return true;
2073 if (parent instanceof AST_DestructuredKeyVal) return parent.value === node;
2074 }
2075
2076 function should_stop(node, parent) {
2077 if (node === rvalue) return true;
2078 if (parent instanceof AST_For) {
2079 if (node !== parent.init) return true;
2080 }
2081 if (node instanceof AST_Assign) {
2082 return node.operator != "=" && lhs.equivalent_to(node.left);
2083 }
2084 if (node instanceof AST_Call) {
2085 if (!(lhs instanceof AST_PropAccess)) return false;
2086 if (!lhs.equivalent_to(node.expression)) return false;
2087 return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
2088 }
2089 if (node instanceof AST_Class) return !compressor.has_directive("use strict");
2090 if (node instanceof AST_Debugger) return true;
2091 if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
2092 if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node;
2093 if (node instanceof AST_DWLoop) return true;
2094 if (node instanceof AST_LoopControl) return true;
2095 if (node instanceof AST_SymbolRef) {
2096 if (node.is_declared(compressor)) {
2097 if (node.fixed_value()) return false;
2098 if (can_drop_symbol(node)) {
2099 return !(parent instanceof AST_PropAccess && parent.expression === node)
2100 && is_arguments(node.definition());
2101 }
2102 } else if (is_direct_assignment(node, parent)) {
2103 return false;
2104 }
2105 if (!replace_all) return true;
2106 scan_rhs = false;
2107 return false;
2108 }
2109 if (node instanceof AST_Try) return true;
2110 if (node instanceof AST_With) return true;
2111 return false;
2112 }
2113
2114 function in_conditional(node, parent) {
2115 if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
2116 if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
2117 if (parent instanceof AST_Call) return parent.optional && parent.expression !== node;
2118 if (parent instanceof AST_Case) return parent.expression !== node;
2119 if (parent instanceof AST_Conditional) return parent.condition !== node;
2120 if (parent instanceof AST_If) return parent.condition !== node;
2121 if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node;
2122 }
2123
2124 function is_last_node(node, parent) {
2125 if (node instanceof AST_Await) return true;
2126 if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right);
2127 if (node instanceof AST_Call) {
2128 var def, fn = node.expression;
2129 if (fn instanceof AST_SymbolRef) {
2130 def = fn.definition();
2131 fn = fn.fixed_value();
2132 }
2133 if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor);
2134 if (def && recursive_ref(compressor, def)) return true;
2135 if (fn.collapse_scanning) return false;
2136 fn.collapse_scanning = true;
2137 var replace = can_replace;
2138 can_replace = false;
2139 var after = stop_after;
2140 var if_hit = stop_if_hit;
2141 if (!all(fn.argnames, function(argname) {
2142 if (argname instanceof AST_DefaultValue) {
2143 argname.value.transform(scanner);
2144 if (abort) return false;
2145 argname = argname.name;
2146 }
2147 return !(argname instanceof AST_Destructured);
2148 })) {
2149 abort = true;
2150 } else if (is_arrow(fn) && fn.value) {
2151 fn.value.transform(scanner);
2152 } else for (var i = 0; !abort && i < fn.body.length; i++) {
2153 var stat = fn.body[i];
2154 if (stat instanceof AST_Return) {
2155 if (stat.value) stat.value.transform(scanner);
2156 break;
2157 }
2158 stat.transform(scanner);
2159 }
2160 stop_if_hit = if_hit;
2161 stop_after = after;
2162 can_replace = replace;
2163 delete fn.collapse_scanning;
2164 if (!abort) return false;
2165 abort = false;
2166 return true;
2167 }
2168 if (node instanceof AST_Exit) {
2169 if (in_try) {
2170 if (in_try.bfinally) return true;
2171 if (in_try.bcatch && node instanceof AST_Throw) return true;
2172 }
2173 return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
2174 }
2175 if (node instanceof AST_Function) {
2176 return compressor.option("ie8") && node.name && lvalues.has(node.name.name);
2177 }
2178 if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent);
2179 if (node instanceof AST_PropAccess) {
2180 var exp = node.expression;
2181 return side_effects
2182 || exp instanceof AST_SymbolRef && is_arguments(exp.definition())
2183 || !value_def && (in_try || !lhs_local)
2184 && !node.optional && exp.may_throw_on_access(compressor);
2185 }
2186 if (node instanceof AST_Spread) return true;
2187 if (node instanceof AST_SymbolRef) {
2188 if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent);
2189 if (side_effects && may_modify(node)) return true;
2190 var def = node.definition();
2191 return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
2192 }
2193 if (node instanceof AST_Template) return !node.is_expr_pure(compressor);
2194 if (node instanceof AST_VarDef) {
2195 if (check_destructured(node.name)) return true;
2196 return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
2197 return node instanceof AST_SymbolDeclaration
2198 && (lvalues.has(node.name) || side_effects && may_modify(node));
2199 }, true);
2200 }
2201 if (node instanceof AST_Yield) return true;
2202 var sym = is_lhs(node.left, node);
2203 if (!sym) return false;
2204 if (sym instanceof AST_PropAccess) return true;
2205 if (check_destructured(sym)) return true;
2206 return sym.match_symbol(function(node) {
2207 return node instanceof AST_SymbolRef
2208 && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition()));
2209 }, true);
2210 }
2211
2212 function may_throw_destructured(node, value) {
2213 if (!value) return !(node instanceof AST_Symbol);
2214 if (node instanceof AST_DefaultValue) {
2215 return value.has_side_effects(compressor)
2216 || node.value.has_side_effects(compressor)
2217 || may_throw_destructured(node.name, is_undefined(value) && node.value);
2218 }
2219 if (node instanceof AST_Destructured) {
2220 if (node.rest && may_throw_destructured(node.rest)) return true;
2221 if (node instanceof AST_DestructuredArray) {
2222 if (!(value instanceof AST_Array || value.is_string(compressor))) return true;
2223 return !all(node.elements, function(element) {
2224 return !may_throw_destructured(element);
2225 });
2226 }
2227 if (node instanceof AST_DestructuredObject) {
2228 if (!value.is_defined(compressor)) return true;
2229 return !all(node.properties, function(prop) {
2230 if (prop instanceof AST_Node && prop.has_side_effects(compressor)) return false;
2231 return !may_throw_destructured(prop.value);
2232 });
2233 }
2234 }
2235 }
2236
2237 function extract_args() {
2238 var iife, fn = compressor.self();
2239 if (fn instanceof AST_LambdaExpression
2240 && !is_generator(fn)
2241 && !fn.uses_arguments
2242 && !fn.pinned()
2243 && (iife = compressor.parent()) instanceof AST_Call
2244 && iife.expression === fn
2245 && is_iife_single(iife)
2246 && all(iife.args, function(arg) {
2247 return !(arg instanceof AST_Spread);
2248 })) {
2249 var fn_strict = compressor.has_directive("use strict");
2250 if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
2251 var has_await = is_async(fn) ? function(node) {
2252 return node instanceof AST_Symbol && node.name == "await";
2253 } : function(node) {
2254 return node instanceof AST_Await && !tw.find_parent(AST_Scope);
2255 };
2256 var arg_scope = null;
2257 var tw = new TreeWalker(function(node, descend) {
2258 if (!arg) return true;
2259 if (has_await(node) || node instanceof AST_Yield) {
2260 arg = null;
2261 return true;
2262 }
2263 if (node instanceof AST_ObjectIdentity && (fn_strict || !arg_scope)) {
2264 arg = null;
2265 return true;
2266 }
2267 if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
2268 var s = node.definition().scope;
2269 if (s !== scope) while (s = s.parent_scope) {
2270 if (s === scope) return true;
2271 }
2272 arg = null;
2273 }
2274 if (node instanceof AST_Scope && !is_arrow(node)) {
2275 var save_scope = arg_scope;
2276 arg_scope = node;
2277 descend();
2278 arg_scope = save_scope;
2279 return true;
2280 }
2281 });
2282 args = iife.args.slice();
2283 var len = args.length;
2284 var names = Object.create(null);
2285 for (var i = fn.argnames.length; --i >= 0;) {
2286 var sym = fn.argnames[i];
2287 var arg = args[i];
2288 var value;
2289 if (sym instanceof AST_DefaultValue) {
2290 value = sym.value;
2291 sym = sym.name;
2292 args[len + i] = value;
2293 }
2294 if (sym instanceof AST_Destructured) {
2295 if (!may_throw_destructured(sym, arg)) continue;
2296 candidates.length = 0;
2297 break;
2298 }
2299 if (sym.name in names) continue;
2300 names[sym.name] = true;
2301 if (value) arg = !arg || is_undefined(arg) ? value : null;
2302 if (!arg && !value) {
2303 arg = make_node(AST_Undefined, sym).transform(compressor);
2304 } else if (arg instanceof AST_Lambda && arg.pinned()) {
2305 arg = null;
2306 } else if (arg) {
2307 arg.walk(tw);
2308 }
2309 if (!arg) continue;
2310 var candidate = make_node(AST_VarDef, sym, {
2311 name: sym,
2312 value: arg,
2313 });
2314 candidate.name_index = i;
2315 candidate.arg_index = value ? len + i : i;
2316 candidates.unshift([ candidate ]);
2317 }
2318 }
2319 }
2320
2321 function extract_candidates(expr, unused) {
2322 hit_stack.push(expr);
2323 if (expr instanceof AST_Array) {
2324 expr.elements.forEach(function(node) {
2325 extract_candidates(node, unused);
2326 });
2327 } else if (expr instanceof AST_Assign) {
2328 var lhs = expr.left;
2329 if (!(lhs instanceof AST_Destructured)) candidates.push(hit_stack.slice());
2330 extract_candidates(lhs);
2331 extract_candidates(expr.right);
2332 if (lhs instanceof AST_SymbolRef && expr.operator == "=") {
2333 assignments[lhs.name] = (assignments[lhs.name] || 0) + 1;
2334 }
2335 } else if (expr instanceof AST_Await) {
2336 extract_candidates(expr.expression, unused);
2337 } else if (expr instanceof AST_Binary) {
2338 var lazy = lazy_op[expr.operator];
2339 if (unused
2340 && lazy
2341 && expr.operator != "??"
2342 && expr.right instanceof AST_Assign
2343 && expr.right.operator == "="
2344 && !(expr.right.left instanceof AST_Destructured)) {
2345 candidates.push(hit_stack.slice());
2346 }
2347 extract_candidates(expr.left, !lazy && unused);
2348 extract_candidates(expr.right, unused);
2349 } else if (expr instanceof AST_Call) {
2350 extract_candidates(expr.expression);
2351 expr.args.forEach(extract_candidates);
2352 } else if (expr instanceof AST_Case) {
2353 extract_candidates(expr.expression);
2354 } else if (expr instanceof AST_Conditional) {
2355 extract_candidates(expr.condition);
2356 extract_candidates(expr.consequent, unused);
2357 extract_candidates(expr.alternative, unused);
2358 } else if (expr instanceof AST_Definitions) {
2359 expr.definitions.forEach(extract_candidates);
2360 } else if (expr instanceof AST_Dot) {
2361 extract_candidates(expr.expression);
2362 } else if (expr instanceof AST_DWLoop) {
2363 extract_candidates(expr.condition);
2364 if (!(expr.body instanceof AST_Block)) {
2365 extract_candidates(expr.body);
2366 }
2367 } else if (expr instanceof AST_Exit) {
2368 if (expr.value) extract_candidates(expr.value);
2369 } else if (expr instanceof AST_For) {
2370 if (expr.init) extract_candidates(expr.init, true);
2371 if (expr.condition) extract_candidates(expr.condition);
2372 if (expr.step) extract_candidates(expr.step, true);
2373 if (!(expr.body instanceof AST_Block)) {
2374 extract_candidates(expr.body);
2375 }
2376 } else if (expr instanceof AST_ForEnumeration) {
2377 extract_candidates(expr.object);
2378 if (!(expr.body instanceof AST_Block)) {
2379 extract_candidates(expr.body);
2380 }
2381 } else if (expr instanceof AST_If) {
2382 extract_candidates(expr.condition);
2383 if (!(expr.body instanceof AST_Block)) {
2384 extract_candidates(expr.body);
2385 }
2386 if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
2387 extract_candidates(expr.alternative);
2388 }
2389 } else if (expr instanceof AST_Object) {
2390 expr.properties.forEach(function(prop) {
2391 hit_stack.push(prop);
2392 if (prop.key instanceof AST_Node) extract_candidates(prop.key);
2393 if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value, unused);
2394 hit_stack.pop();
2395 });
2396 } else if (expr instanceof AST_Sequence) {
2397 var end = expr.expressions.length - (unused ? 0 : 1);
2398 expr.expressions.forEach(function(node, index) {
2399 extract_candidates(node, index < end);
2400 });
2401 } else if (expr instanceof AST_SimpleStatement) {
2402 extract_candidates(expr.body, true);
2403 } else if (expr instanceof AST_Spread) {
2404 extract_candidates(expr.expression);
2405 } else if (expr instanceof AST_Sub) {
2406 extract_candidates(expr.expression);
2407 extract_candidates(expr.property);
2408 } else if (expr instanceof AST_Switch) {
2409 extract_candidates(expr.expression);
2410 expr.body.forEach(extract_candidates);
2411 } else if (expr instanceof AST_Unary) {
2412 if (UNARY_POSTFIX[expr.operator]) {
2413 candidates.push(hit_stack.slice());
2414 } else {
2415 extract_candidates(expr.expression);
2416 }
2417 } else if (expr instanceof AST_VarDef) {
2418 if (expr.name instanceof AST_SymbolVar) {
2419 if (expr.value) {
2420 var def = expr.name.definition();
2421 if (def.references.length > def.replaced) {
2422 candidates.push(hit_stack.slice());
2423 }
2424 } else {
2425 declare_only[expr.name.name] = (declare_only[expr.name.name] || 0) + 1;
2426 }
2427 }
2428 if (expr.value) extract_candidates(expr.value);
2429 } else if (expr instanceof AST_Yield) {
2430 if (expr.expression) extract_candidates(expr.expression);
2431 }
2432 hit_stack.pop();
2433 }
2434
2435 function find_stop(node, level) {
2436 var parent = scanner.parent(level);
2437 if (parent instanceof AST_Array) return node;
2438 if (parent instanceof AST_Assign) return node;
2439 if (parent instanceof AST_Await) return node;
2440 if (parent instanceof AST_Binary) return node;
2441 if (parent instanceof AST_Call) return node;
2442 if (parent instanceof AST_Case) return node;
2443 if (parent instanceof AST_Conditional) return node;
2444 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2445 if (parent instanceof AST_Exit) return node;
2446 if (parent instanceof AST_If) return node;
2447 if (parent instanceof AST_IterationStatement) return node;
2448 if (parent instanceof AST_ObjectProperty) return node;
2449 if (parent instanceof AST_PropAccess) return node;
2450 if (parent instanceof AST_Sequence) {
2451 return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
2452 }
2453 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2454 if (parent instanceof AST_Spread) return node;
2455 if (parent instanceof AST_Switch) return node;
2456 if (parent instanceof AST_Unary) return node;
2457 if (parent instanceof AST_VarDef) return node;
2458 if (parent instanceof AST_Yield) return node;
2459 return null;
2460 }
2461
2462 function find_stop_logical(parent, op, level) {
2463 var node;
2464 do {
2465 node = parent;
2466 parent = scanner.parent(++level);
2467 } while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op
2468 || parent instanceof AST_Binary && parent.operator == op);
2469 return node;
2470 }
2471
2472 function find_stop_expr(expr, cont, node, parent, level) {
2473 var replace = can_replace;
2474 can_replace = false;
2475 var after = stop_after;
2476 var if_hit = stop_if_hit;
2477 var stack = scanner.stack;
2478 scanner.stack = [ parent ];
2479 expr.transform(scanner);
2480 scanner.stack = stack;
2481 stop_if_hit = if_hit;
2482 stop_after = after;
2483 can_replace = replace;
2484 if (abort) {
2485 abort = false;
2486 return node;
2487 }
2488 return cont(parent, level + 1);
2489 }
2490
2491 function find_stop_value(node, level) {
2492 var parent = scanner.parent(level);
2493 if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
2494 if (parent instanceof AST_Assign) {
2495 if (may_throw(parent)) return node;
2496 if (parent.left.match_symbol(function(ref) {
2497 return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name);
2498 })) return node;
2499 var op;
2500 if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) {
2501 return find_stop_value(parent, level + 1);
2502 }
2503 return find_stop_logical(parent, op, level);
2504 }
2505 if (parent instanceof AST_Binary) {
2506 var op;
2507 if (parent.left === node || !lazy_op[op = parent.operator]) {
2508 return find_stop_value(parent, level + 1);
2509 }
2510 return find_stop_logical(parent, op, level);
2511 }
2512 if (parent instanceof AST_Call) return parent;
2513 if (parent instanceof AST_Case) {
2514 if (parent.expression !== node) return node;
2515 return find_stop_value(parent, level + 1);
2516 }
2517 if (parent instanceof AST_Conditional) {
2518 if (parent.condition !== node) return node;
2519 return find_stop_value(parent, level + 1);
2520 }
2521 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2522 if (parent instanceof AST_Do) return node;
2523 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2524 if (parent instanceof AST_For) {
2525 if (parent.init !== node && parent.condition !== node) return node;
2526 return find_stop_value(parent, level + 1);
2527 }
2528 if (parent instanceof AST_ForEnumeration) {
2529 if (parent.init !== node) return node;
2530 return find_stop_value(parent, level + 1);
2531 }
2532 if (parent instanceof AST_If) {
2533 if (parent.condition !== node) return node;
2534 return find_stop_value(parent, level + 1);
2535 }
2536 if (parent instanceof AST_ObjectProperty) {
2537 var obj = scanner.parent(level + 1);
2538 return all(obj.properties, function(prop) {
2539 return prop instanceof AST_ObjectKeyVal;
2540 }) ? find_stop_value(obj, level + 2) : obj;
2541 }
2542 if (parent instanceof AST_PropAccess) {
2543 var exp = parent.expression;
2544 if (exp === node) return find_stop_value(parent, level + 1);
2545 return find_stop_expr(exp, find_stop_value, node, parent, level);
2546 }
2547 if (parent instanceof AST_Sequence) {
2548 return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
2549 }
2550 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2551 if (parent instanceof AST_Spread) return find_stop_value(parent, level + 1);
2552 if (parent instanceof AST_Switch) {
2553 if (parent.expression !== node) return node;
2554 return find_stop_value(parent, level + 1);
2555 }
2556 if (parent instanceof AST_Unary) {
2557 if (parent.operator == "delete") return node;
2558 return find_stop_value(parent, level + 1);
2559 }
2560 if (parent instanceof AST_VarDef) return parent.name.match_symbol(function(sym) {
2561 return sym instanceof AST_SymbolDeclaration && (lhs.name == sym.name || value_def.name == sym.name);
2562 }) ? node : find_stop_value(parent, level + 1);
2563 if (parent instanceof AST_While) {
2564 if (parent.condition !== node) return node;
2565 return find_stop_value(parent, level + 1);
2566 }
2567 if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1);
2568 return null;
2569 }
2570
2571 function find_stop_unused(node, level) {
2572 var parent = scanner.parent(level);
2573 if (is_last_node(node, parent)) return node;
2574 if (in_conditional(node, parent)) return node;
2575 if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
2576 if (parent instanceof AST_Assign) return check_assignment(parent.left);
2577 if (parent instanceof AST_Await) return node;
2578 if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
2579 if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
2580 if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
2581 if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
2582 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2583 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2584 if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
2585 if (parent instanceof AST_IterationStatement) return node;
2586 if (parent instanceof AST_ObjectProperty) {
2587 var obj = scanner.parent(level + 1);
2588 return all(obj.properties, function(prop) {
2589 return prop instanceof AST_ObjectKeyVal;
2590 }) ? find_stop_unused(obj, level + 2) : obj;
2591 }
2592 if (parent instanceof AST_PropAccess) {
2593 var exp = parent.expression;
2594 if (exp === node) return find_stop_unused(parent, level + 1);
2595 return find_stop_expr(exp, find_stop_unused, node, parent, level);
2596 }
2597 if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
2598 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2599 if (parent instanceof AST_Spread) return node;
2600 if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
2601 if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
2602 if (parent instanceof AST_VarDef) return check_assignment(parent.name);
2603 if (parent instanceof AST_Yield) return node;
2604 return null;
2605
2606 function check_assignment(lhs) {
2607 if (may_throw(parent)) return node;
2608 if (lhs !== node && lhs instanceof AST_Destructured) {
2609 return find_stop_expr(lhs, find_stop_unused, node, parent, level);
2610 }
2611 return find_stop_unused(parent, level + 1);
2612 }
2613 }
2614
2615 function mangleable_var(rhs) {
2616 if (force_single) {
2617 force_single = false;
2618 return;
2619 }
2620 if (remaining < 1) return;
2621 var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
2622 if (!(value instanceof AST_SymbolRef)) return;
2623 var def = value.definition();
2624 if (def.undeclared) return;
2625 if (is_arguments(def)) return;
2626 if (value !== rhs) {
2627 if (is_lhs_read_only(value, compressor)) return;
2628 var referenced = def.references.length - def.replaced;
2629 if (referenced < 2) return;
2630 var expr = candidate.clone();
2631 expr[expr instanceof AST_Assign ? "right" : "value"] = value;
2632 if (candidate.name_index >= 0) {
2633 expr.name_index = candidate.name_index;
2634 expr.arg_index = candidate.arg_index;
2635 }
2636 candidate = expr;
2637 }
2638 return value_def = def;
2639 }
2640
2641 function remaining_refs(def) {
2642 return def.references.length - def.replaced - (assignments[def.name] || 0);
2643 }
2644
2645 function get_lhs(expr) {
2646 if (expr instanceof AST_Assign) {
2647 var lhs = expr.left;
2648 if (expr.operator != "=") return lhs;
2649 if (!(lhs instanceof AST_SymbolRef)) return lhs;
2650 var def = lhs.definition();
2651 if (scope.uses_arguments && is_funarg(def)) return lhs;
2652 if (compressor.exposed(def)) return lhs;
2653 remaining = remaining_refs(def);
2654 if (def.fixed && lhs.fixed) {
2655 var matches = def.references.filter(function(ref) {
2656 return ref.fixed === lhs.fixed;
2657 }).length - 1;
2658 if (matches < remaining) {
2659 remaining = matches;
2660 assign_pos = 0;
2661 }
2662 }
2663 mangleable_var(expr.right);
2664 return lhs;
2665 }
2666 if (expr instanceof AST_Binary) return expr.right.left;
2667 if (expr instanceof AST_Unary) return expr.expression;
2668 if (expr instanceof AST_VarDef) {
2669 var lhs = expr.name;
2670 var def = lhs.definition();
2671 if (def.const_redefs) return;
2672 if (!member(lhs, def.orig)) return;
2673 if (scope.uses_arguments && is_funarg(def)) return;
2674 var declared = def.orig.length - def.eliminated - (declare_only[def.name] || 0);
2675 remaining = remaining_refs(def);
2676 if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) {
2677 if (!ref.fixed) return true;
2678 if (!ref.fixed.assigns) return true;
2679 var assign = ref.fixed.assigns[0];
2680 return assign === lhs || get_rvalue(assign) === expr.value;
2681 }).length);
2682 if (declared > 1 && !(lhs instanceof AST_SymbolFunarg)) {
2683 mangleable_var(expr.value);
2684 return make_node(AST_SymbolRef, lhs, lhs);
2685 }
2686 if (mangleable_var(expr.value) || remaining == 1 && !compressor.exposed(def)) {
2687 return make_node(AST_SymbolRef, lhs, lhs);
2688 }
2689 return;
2690 }
2691 }
2692
2693 function get_rvalue(expr) {
2694 if (expr instanceof AST_Assign) return expr.right;
2695 if (expr instanceof AST_Binary) {
2696 var node = expr.clone();
2697 node.right = expr.right.right;
2698 return node;
2699 }
2700 if (expr instanceof AST_VarDef) return expr.value;
2701 }
2702
2703 function invariant(expr) {
2704 if (expr instanceof AST_Array) return false;
2705 if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
2706 return invariant(expr.left) && invariant(expr.right);
2707 }
2708 if (expr instanceof AST_Call) return false;
2709 if (expr instanceof AST_Conditional) {
2710 return invariant(expr.consequent) && invariant(expr.alternative);
2711 }
2712 if (expr instanceof AST_Object) return false;
2713 return !expr.has_side_effects(compressor);
2714 }
2715
2716 function foldable(expr) {
2717 if (expr instanceof AST_Assign && expr.right.single_use) return;
2718 var lhs_ids = Object.create(null);
2719 var marker = new TreeWalker(function(node) {
2720 if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true;
2721 });
2722 while (expr instanceof AST_Assign && expr.operator == "=") {
2723 expr.left.walk(marker);
2724 expr = expr.right;
2725 }
2726 if (expr instanceof AST_ObjectIdentity) return rhs_exact_match;
2727 if (expr instanceof AST_SymbolRef) {
2728 var value = expr.evaluate(compressor);
2729 if (value === expr) return rhs_exact_match;
2730 return rhs_fuzzy_match(value, rhs_exact_match);
2731 }
2732 if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
2733 if (expr.is_constant()) {
2734 var ev = expr.evaluate(compressor);
2735 if (!(ev instanceof AST_Node)) return rhs_fuzzy_match(ev, rhs_exact_match);
2736 }
2737 if (!(lhs instanceof AST_SymbolRef)) return false;
2738 if (!invariant(expr)) return false;
2739 var circular;
2740 expr.walk(new TreeWalker(function(node) {
2741 if (circular) return true;
2742 if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true;
2743 }));
2744 return !circular && rhs_exact_match;
2745
2746 function rhs_exact_match(node) {
2747 return expr.equivalent_to(node);
2748 }
2749 }
2750
2751 function rhs_fuzzy_match(value, fallback) {
2752 return function(node, tw) {
2753 if (tw.in_boolean_context()) {
2754 if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
2755 return true;
2756 }
2757 if (node.is_constant()) {
2758 var ev = node.evaluate(compressor);
2759 if (!(ev instanceof AST_Node)) return !ev == !value;
2760 }
2761 }
2762 return fallback(node);
2763 };
2764 }
2765
2766 function may_be_global(node) {
2767 if (node instanceof AST_SymbolRef) {
2768 node = node.fixed_value();
2769 if (!node) return true;
2770 }
2771 if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
2772 return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity;
2773 }
2774
2775 function get_lvalues(expr) {
2776 var lvalues = new Dictionary();
2777 if (expr instanceof AST_VarDef) lvalues.add(expr.name.name, lhs);
2778 var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict");
2779 var scan_toplevel = scope instanceof AST_Toplevel;
2780 var tw = new TreeWalker(function(node) {
2781 var value;
2782 if (node instanceof AST_SymbolRef) {
2783 value = node.fixed_value() || node;
2784 } else if (node instanceof AST_ObjectIdentity) {
2785 value = node;
2786 }
2787 if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
2788 if (find_arguments && node instanceof AST_Sub) {
2789 scope.each_argname(function(argname) {
2790 if (!compressor.option("reduce_vars") || argname.definition().assignments) {
2791 lvalues.add(argname.name, true);
2792 }
2793 });
2794 find_arguments = false;
2795 }
2796 if (!scan_toplevel) return;
2797 if (node.TYPE == "Call") {
2798 if (modify_toplevel) return;
2799 var exp = node.expression;
2800 if (exp instanceof AST_PropAccess) return;
2801 if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return;
2802 modify_toplevel = true;
2803 } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
2804 if (node === lhs && !(expr instanceof AST_Unary)) {
2805 modify_toplevel = true;
2806 } else {
2807 read_toplevel = true;
2808 }
2809 }
2810 });
2811 expr.walk(tw);
2812 return lvalues;
2813 }
2814
2815 function remove_candidate(expr) {
2816 var index = expr.name_index;
2817 if (index >= 0) {
2818 var argname = scope.argnames[index];
2819 if (argname instanceof AST_DefaultValue) {
2820 argname.value = make_node(AST_Number, argname, {
2821 value: 0
2822 });
2823 argname.name.definition().fixed = false;
2824 } else {
2825 var args = compressor.parent().args;
2826 if (args[index]) {
2827 args[index] = make_node(AST_Number, args[index], {
2828 value: 0
2829 });
2830 argname.definition().fixed = false;
2831 }
2832 }
2833 return true;
2834 }
2835 var end = hit_stack.length - 1;
2836 if (hit_stack[end - 1].body === hit_stack[end]) end--;
2837 var tt = new TreeTransformer(function(node, descend, in_list) {
2838 if (hit) return node;
2839 if (node !== hit_stack[hit_index]) return node;
2840 hit_index++;
2841 if (hit_index <= end) return handle_custom_scan_order(node, tt);
2842 hit = true;
2843 if (node instanceof AST_VarDef) {
2844 declare_only[node.name.name] = (declare_only[node.name.name] || 0) + 1;
2845 if (value_def) value_def.replaced++;
2846 node = node.clone();
2847 node.value = null;
2848 return node;
2849 }
2850 return in_list ? List.skip : null;
2851 }, patch_sequence);
2852 abort = false;
2853 hit = false;
2854 hit_index = 0;
2855 return statements[stat_index].transform(tt);
2856 }
2857
2858 function patch_sequence(node) {
2859 if (node instanceof AST_Sequence) switch (node.expressions.length) {
2860 case 0: return null;
2861 case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]);
2862 }
2863 }
2864
2865 function is_lhs_local(lhs) {
2866 var sym = root_expr(lhs);
2867 return sym instanceof AST_SymbolRef
2868 && sym.definition().scope === scope
2869 && !(in_loop
2870 && (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
2871 || candidate instanceof AST_Unary
2872 || candidate instanceof AST_Assign && candidate.operator != "="));
2873 }
2874
2875 function value_has_side_effects() {
2876 if (candidate instanceof AST_Unary) return false;
2877 return rvalue.has_side_effects(compressor);
2878 }
2879
2880 function replace_all_symbols(expr) {
2881 if (expr instanceof AST_Unary) return false;
2882 if (side_effects) return false;
2883 if (value_def) return true;
2884 if (!(lhs instanceof AST_SymbolRef)) return false;
2885 var referenced;
2886 if (expr instanceof AST_VarDef) {
2887 referenced = 1;
2888 } else if (expr.operator == "=") {
2889 referenced = 2;
2890 } else {
2891 return false;
2892 }
2893 var def = lhs.definition();
2894 return def.references.length - def.replaced == referenced;
2895 }
2896
2897 function symbol_in_lvalues(sym, parent) {
2898 var lvalue = lvalues.get(sym.name);
2899 if (!lvalue || all(lvalue, function(lhs) {
2900 return !lhs;
2901 })) return;
2902 if (lvalue[0] !== lhs) return true;
2903 scan_rhs = false;
2904 }
2905
2906 function may_modify(sym) {
2907 var def = sym.definition();
2908 if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
2909 if (def.scope !== scope) return true;
2910 if (modify_toplevel && compressor.exposed(def)) return true;
2911 return !all(def.references, function(ref) {
2912 return ref.scope.resolve() === scope;
2913 });
2914 }
2915
2916 function side_effects_external(node, lhs) {
2917 if (node instanceof AST_Assign) return side_effects_external(node.left, true);
2918 if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
2919 if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
2920 if (lhs) {
2921 if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
2922 if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
2923 if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
2924 }
2925 return false;
2926 }
2927 }
2928
2929 function eliminate_spurious_blocks(statements) {
2930 var seen_dirs = [];
2931 for (var i = 0; i < statements.length;) {
2932 var stat = statements[i];
2933 if (stat instanceof AST_BlockStatement) {
2934 if (all(stat.body, safe_to_trim)) {
2935 CHANGED = true;
2936 eliminate_spurious_blocks(stat.body);
2937 [].splice.apply(statements, [i, 1].concat(stat.body));
2938 i += stat.body.length;
2939 continue;
2940 }
2941 }
2942 if (stat instanceof AST_Directive) {
2943 if (member(stat.value, seen_dirs)) {
2944 CHANGED = true;
2945 statements.splice(i, 1);
2946 continue;
2947 }
2948 seen_dirs.push(stat.value);
2949 }
2950 if (stat instanceof AST_EmptyStatement) {
2951 CHANGED = true;
2952 statements.splice(i, 1);
2953 continue;
2954 }
2955 i++;
2956 }
2957 }
2958
2959 function handle_if_return(statements, compressor) {
2960 var self = compressor.self();
2961 var parent = compressor.parent();
2962 var in_lambda = last_of(function(node) {
2963 return node instanceof AST_Lambda;
2964 });
2965 var in_iife = in_lambda && parent && parent.TYPE == "Call";
2966 var multiple_if_returns = has_multiple_if_returns(statements);
2967 for (var i = statements.length; --i >= 0;) {
2968 var stat = statements[i];
2969 var j = next_index(i);
2970 var next = statements[j];
2971
2972 if (in_lambda && !next && stat instanceof AST_Return) {
2973 if (!stat.value) {
2974 CHANGED = true;
2975 statements.splice(i, 1);
2976 continue;
2977 }
2978 var tail = stat.value.tail_node();
2979 if (tail instanceof AST_UnaryPrefix && tail.operator == "void") {
2980 CHANGED = true;
2981 var body;
2982 if (tail === stat.value) {
2983 body = tail.expression;
2984 } else {
2985 body = stat.value.clone();
2986 body.expressions[body.length - 1] = tail.expression;
2987 }
2988 statements[i] = make_node(AST_SimpleStatement, stat, {
2989 body: body,
2990 });
2991 continue;
2992 }
2993 }
2994
2995 if (stat instanceof AST_If) {
2996 var ab = aborts(stat.body);
2997 if (can_merge_flow(ab)) {
2998 if (ab.label) remove(ab.label.thedef.references, ab);
2999 CHANGED = true;
3000 stat = stat.clone();
3001 stat.condition = stat.condition.negate(compressor);
3002 var body = as_statement_array_with_return(stat.body, ab);
3003 stat.body = make_node(AST_BlockStatement, stat, {
3004 body: as_statement_array(stat.alternative).concat(extract_functions())
3005 });
3006 stat.alternative = make_node(AST_BlockStatement, stat, {
3007 body: body
3008 });
3009 statements[i] = stat;
3010 statements[i] = stat.transform(compressor);
3011 continue;
3012 }
3013
3014 if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) {
3015 var negated = stat.condition.negate(compressor);
3016 if (negated.print_to_string().length <= stat.condition.print_to_string().length) {
3017 CHANGED = true;
3018 stat = stat.clone();
3019 stat.condition = negated;
3020 statements[j] = stat.body;
3021 stat.body = next;
3022 statements[i] = stat;
3023 statements[i] = stat.transform(compressor);
3024 continue;
3025 }
3026 }
3027
3028 var alt = aborts(stat.alternative);
3029 if (can_merge_flow(alt)) {
3030 if (alt.label) remove(alt.label.thedef.references, alt);
3031 CHANGED = true;
3032 stat = stat.clone();
3033 stat.body = make_node(AST_BlockStatement, stat.body, {
3034 body: as_statement_array(stat.body).concat(extract_functions())
3035 });
3036 var body = as_statement_array_with_return(stat.alternative, alt);
3037 stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
3038 body: body
3039 });
3040 statements[i] = stat;
3041 statements[i] = stat.transform(compressor);
3042 continue;
3043 }
3044
3045 if (compressor.option("typeofs")) {
3046 if (ab && !alt) {
3047 mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
3048 body: statements.slice(i + 1)
3049 }));
3050 }
3051 if (!ab && alt) {
3052 mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
3053 body: statements.slice(i + 1)
3054 }));
3055 }
3056 }
3057 }
3058
3059 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3060 var value = stat.body.value;
3061 var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
3062 //---
3063 // pretty silly case, but:
3064 // if (foo()) return; return; ---> foo(); return;
3065 if (!value && !stat.alternative
3066 && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
3067 CHANGED = true;
3068 statements[i] = make_node(AST_SimpleStatement, stat.condition, {
3069 body: stat.condition
3070 });
3071 continue;
3072 }
3073 //---
3074 // if (foo()) return x; return y; ---> return foo() ? x : y;
3075 if (!stat.alternative && next instanceof AST_Return) {
3076 CHANGED = true;
3077 stat = stat.clone();
3078 stat.alternative = next;
3079 statements.splice(i, 1, stat.transform(compressor));
3080 statements.splice(j, 1);
3081 continue;
3082 }
3083 //---
3084 // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined;
3085 if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
3086 CHANGED = true;
3087 stat = stat.clone();
3088 stat.alternative = make_node(AST_Return, stat, {
3089 value: null
3090 });
3091 statements.splice(i, 1, stat.transform(compressor));
3092 continue;
3093 }
3094 //---
3095 // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e;
3096 //
3097 // if sequences is not enabled, this can lead to an endless loop (issue #866).
3098 // however, with sequences on this helps producing slightly better output for
3099 // the example code.
3100 var prev = statements[prev_index(i)];
3101 if (compressor.option("sequences") && in_lambda && !stat.alternative
3102 && (!prev && in_iife || prev instanceof AST_If && prev.body instanceof AST_Return)
3103 && next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
3104 CHANGED = true;
3105 stat = stat.clone();
3106 stat.alternative = make_node(AST_BlockStatement, next, {
3107 body: [
3108 next,
3109 make_node(AST_Return, next, {
3110 value: null
3111 })
3112 ]
3113 });
3114 statements.splice(i, 1, stat.transform(compressor));
3115 statements.splice(j, 1);
3116 continue;
3117 }
3118 }
3119 }
3120
3121 function has_multiple_if_returns(statements) {
3122 var n = 0;
3123 for (var i = statements.length; --i >= 0;) {
3124 var stat = statements[i];
3125 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3126 if (++n > 1) return true;
3127 }
3128 }
3129 return false;
3130 }
3131
3132 function is_return_void(value) {
3133 return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
3134 }
3135
3136 function is_last_statement(body, stat) {
3137 var index = body.lastIndexOf(stat);
3138 if (index < 0) return false;
3139 while (++index < body.length) {
3140 if (!is_declaration(body[index])) return false;
3141 }
3142 return true;
3143 }
3144
3145 function last_of(predicate) {
3146 var block = self, stat, level = 0;
3147 do {
3148 do {
3149 if (predicate(block)) return true;
3150 block = compressor.parent(level++);
3151 } while (block instanceof AST_If && (stat = block));
3152 } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
3153 && is_last_statement(block.body, stat));
3154 }
3155
3156 function match_target(target) {
3157 return last_of(function(node) {
3158 return node === target;
3159 });
3160 }
3161
3162 function can_drop_abort(ab) {
3163 if (ab instanceof AST_Return) return in_lambda && is_return_void(ab.value);
3164 if (!(ab instanceof AST_LoopControl)) return false;
3165 var lct = compressor.loopcontrol_target(ab);
3166 if (ab instanceof AST_Continue) return match_target(loop_body(lct));
3167 if (lct instanceof AST_IterationStatement) return false;
3168 return match_target(lct);
3169 }
3170
3171 function can_merge_flow(ab) {
3172 if (!can_drop_abort(ab)) return false;
3173 for (var j = statements.length; --j > i;) {
3174 var stat = statements[j];
3175 if (stat instanceof AST_DefClass) {
3176 if (stat.name.definition().preinit) return false;
3177 } else if (stat instanceof AST_Const || stat instanceof AST_Let) {
3178 if (!all(stat.definitions, function(defn) {
3179 return !defn.name.match_symbol(function(node) {
3180 return node instanceof AST_SymbolDeclaration && node.definition().preinit;
3181 });
3182 })) return false;
3183 }
3184 }
3185 return true;
3186 }
3187
3188 function extract_functions() {
3189 var defuns = [];
3190 var lexical = false;
3191 var tail = statements.splice(i + 1).filter(function(stat) {
3192 if (stat instanceof AST_LambdaDefinition) {
3193 defuns.push(stat);
3194 return false;
3195 }
3196 if (is_lexical_definition(stat)) lexical = true;
3197 return true;
3198 });
3199 [].push.apply(lexical ? tail : statements, defuns);
3200 return tail;
3201 }
3202
3203 function as_statement_array_with_return(node, ab) {
3204 var body = as_statement_array(node);
3205 var block = body, last;
3206 while ((last = block[block.length - 1]) !== ab) {
3207 block = last.body;
3208 }
3209 block.pop();
3210 if (ab.value) block.push(make_node(AST_SimpleStatement, ab.value, {
3211 body: ab.value.expression
3212 }));
3213 return body;
3214 }
3215
3216 function next_index(i) {
3217 for (var j = i + 1; j < statements.length; j++) {
3218 if (!is_declaration(statements[j])) break;
3219 }
3220 return j;
3221 }
3222
3223 function prev_index(i) {
3224 for (var j = i; --j >= 0;) {
3225 if (!is_declaration(statements[j])) break;
3226 }
3227 return j;
3228 }
3229 }
3230
3231 function eliminate_dead_code(statements, compressor) {
3232 var has_quit;
3233 var self = compressor.self();
3234 for (var i = 0, n = 0, len = statements.length; i < len; i++) {
3235 var stat = statements[i];
3236 if (stat instanceof AST_LoopControl) {
3237 var lct = compressor.loopcontrol_target(stat);
3238 if (stat instanceof AST_Break
3239 && !(lct instanceof AST_IterationStatement)
3240 && loop_body(lct) === self
3241 || stat instanceof AST_Continue
3242 && loop_body(lct) === self) {
3243 if (stat.label) remove(stat.label.thedef.references, stat);
3244 } else {
3245 statements[n++] = stat;
3246 }
3247 } else {
3248 statements[n++] = stat;
3249 }
3250 if (aborts(stat)) {
3251 has_quit = statements.slice(i + 1);
3252 break;
3253 }
3254 }
3255 statements.length = n;
3256 CHANGED = n != len;
3257 if (has_quit) has_quit.forEach(function(stat) {
3258 extract_declarations_from_unreachable_code(compressor, stat, statements);
3259 });
3260 }
3261
3262 function sequencesize(statements, compressor) {
3263 if (statements.length < 2) return;
3264 var seq = [], n = 0;
3265 function push_seq() {
3266 if (!seq.length) return;
3267 var body = make_sequence(seq[0], seq);
3268 statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
3269 seq = [];
3270 }
3271 for (var i = 0, len = statements.length; i < len; i++) {
3272 var stat = statements[i];
3273 if (stat instanceof AST_SimpleStatement) {
3274 if (seq.length >= compressor.sequences_limit) push_seq();
3275 var body = stat.body;
3276 if (seq.length > 0) body = body.drop_side_effect_free(compressor);
3277 if (body) merge_sequence(seq, body);
3278 } else if (is_declaration(stat)) {
3279 statements[n++] = stat;
3280 } else {
3281 push_seq();
3282 statements[n++] = stat;
3283 }
3284 }
3285 push_seq();
3286 statements.length = n;
3287 if (n != len) CHANGED = true;
3288 }
3289
3290 function to_simple_statement(block, decls) {
3291 if (!(block instanceof AST_BlockStatement)) return block;
3292 var stat = null;
3293 for (var i = 0; i < block.body.length; i++) {
3294 var line = block.body[i];
3295 if (line instanceof AST_Var && declarations_only(line)) {
3296 decls.push(line);
3297 } else if (stat || is_lexical_definition(line)) {
3298 return false;
3299 } else {
3300 stat = line;
3301 }
3302 }
3303 return stat;
3304 }
3305
3306 function sequencesize_2(statements, compressor) {
3307 function cons_seq(right) {
3308 n--;
3309 CHANGED = true;
3310 var left = prev.body;
3311 return make_sequence(left, [ left, right ]);
3312 }
3313 var n = 0, prev;
3314 for (var i = 0; i < statements.length; i++) {
3315 var stat = statements[i];
3316 if (prev) {
3317 if (stat instanceof AST_Exit) {
3318 if (stat.value || !in_async_generator(scope)) {
3319 stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).optimize(compressor);
3320 }
3321 } else if (stat instanceof AST_For) {
3322 if (!(stat.init instanceof AST_Definitions)) {
3323 var abort = false;
3324 prev.body.walk(new TreeWalker(function(node) {
3325 if (abort || node instanceof AST_Scope) return true;
3326 if (node instanceof AST_Binary && node.operator == "in") {
3327 abort = true;
3328 return true;
3329 }
3330 }));
3331 if (!abort) {
3332 if (stat.init) stat.init = cons_seq(stat.init);
3333 else {
3334 stat.init = prev.body;
3335 n--;
3336 CHANGED = true;
3337 }
3338 }
3339 }
3340 } else if (stat instanceof AST_ForIn) {
3341 if (!is_lexical_definition(stat.init)) stat.object = cons_seq(stat.object);
3342 } else if (stat instanceof AST_If) {
3343 stat.condition = cons_seq(stat.condition);
3344 } else if (stat instanceof AST_Switch) {
3345 stat.expression = cons_seq(stat.expression);
3346 } else if (stat instanceof AST_With) {
3347 stat.expression = cons_seq(stat.expression);
3348 }
3349 }
3350 if (compressor.option("conditionals") && stat instanceof AST_If) {
3351 var decls = [];
3352 var body = to_simple_statement(stat.body, decls);
3353 var alt = to_simple_statement(stat.alternative, decls);
3354 if (body !== false && alt !== false && decls.length > 0) {
3355 var len = decls.length;
3356 decls.push(make_node(AST_If, stat, {
3357 condition: stat.condition,
3358 body: body || make_node(AST_EmptyStatement, stat.body),
3359 alternative: alt
3360 }));
3361 decls.unshift(n, 1);
3362 [].splice.apply(statements, decls);
3363 i += len;
3364 n += len + 1;
3365 prev = null;
3366 CHANGED = true;
3367 continue;
3368 }
3369 }
3370 statements[n++] = stat;
3371 prev = stat instanceof AST_SimpleStatement ? stat : null;
3372 }
3373 statements.length = n;
3374 }
3375
3376 function extract_exprs(body) {
3377 if (body instanceof AST_Assign) return [ body ];
3378 if (body instanceof AST_Sequence) return body.expressions.slice();
3379 }
3380
3381 function join_assigns(defn, body, keep) {
3382 var exprs = extract_exprs(body);
3383 if (!exprs) return;
3384 var trimmed = false;
3385 for (var i = exprs.length - 1; --i >= 0;) {
3386 var expr = exprs[i];
3387 if (!(expr instanceof AST_Assign)) continue;
3388 if (expr.operator != "=") continue;
3389 if (!(expr.left instanceof AST_SymbolRef)) continue;
3390 var tail = exprs.slice(i + 1);
3391 if (!trim_assigns(expr.left, expr.right, tail)) continue;
3392 trimmed = true;
3393 exprs = exprs.slice(0, i + 1).concat(tail);
3394 }
3395 if (defn instanceof AST_Definitions) {
3396 keep = keep || 0;
3397 for (var i = defn.definitions.length; --i >= 0;) {
3398 var def = defn.definitions[i];
3399 if (!def.value) continue;
3400 if (trim_assigns(def.name, def.value, exprs)) trimmed = true;
3401 if (merge_conditional_assignments(def, exprs, keep)) trimmed = true;
3402 break;
3403 }
3404 if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
3405 }
3406 return trimmed && exprs;
3407 }
3408
3409 function merge_assigns(prev, defn) {
3410 if (!(prev instanceof AST_SimpleStatement)) return;
3411 if (declarations_only(defn)) return;
3412 var exprs = extract_exprs(prev.body);
3413 if (!exprs) return;
3414 var definitions = [];
3415 if (!join_var_assign(definitions, exprs.reverse(), 0)) return;
3416 defn.definitions = definitions.reverse().concat(defn.definitions);
3417 return exprs.reverse();
3418 }
3419
3420 function merge_conditional_assignments(var_def, exprs, keep) {
3421 if (!compressor.option("conditionals")) return;
3422 if (var_def.name instanceof AST_Destructured) return;
3423 var trimmed = false;
3424 var def = var_def.name.definition();
3425 while (exprs.length > keep) {
3426 var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]);
3427 if (!cond) break;
3428 var_def.value = cond;
3429 exprs.shift();
3430 trimmed = true;
3431 }
3432 return trimmed;
3433 }
3434
3435 function join_var_assign(definitions, exprs, keep) {
3436 var trimmed = false;
3437 while (exprs.length > keep) {
3438 var expr = exprs[0];
3439 if (!(expr instanceof AST_Assign)) break;
3440 if (expr.operator != "=") break;
3441 var lhs = expr.left;
3442 if (!(lhs instanceof AST_SymbolRef)) break;
3443 if (is_undeclared_ref(lhs)) break;
3444 if (lhs.scope.resolve() !== scope) break;
3445 var def = lhs.definition();
3446 if (def.scope !== scope) break;
3447 if (def.orig.length > def.eliminated + 1) break;
3448 if (def.orig[0].TYPE != "SymbolVar") break;
3449 var name = make_node(AST_SymbolVar, lhs, lhs);
3450 definitions.push(make_node(AST_VarDef, expr, {
3451 name: name,
3452 value: expr.right
3453 }));
3454 def.orig.push(name);
3455 def.replaced++;
3456 exprs.shift();
3457 trimmed = true;
3458 }
3459 return trimmed;
3460 }
3461
3462 function trim_assigns(name, value, exprs) {
3463 if (!(value instanceof AST_Object)) return;
3464 var trimmed = false;
3465 do {
3466 var node = exprs[0];
3467 if (!(node instanceof AST_Assign)) break;
3468 if (node.operator != "=") break;
3469 if (!(node.left instanceof AST_PropAccess)) break;
3470 var sym = node.left.expression;
3471 if (!(sym instanceof AST_SymbolRef)) break;
3472 if (name.name != sym.name) break;
3473 if (!node.right.is_constant_expression(scope)) break;
3474 var prop = node.left.property;
3475 if (prop instanceof AST_Node) {
3476 prop = prop.evaluate(compressor);
3477 }
3478 if (prop instanceof AST_Node) break;
3479 prop = "" + prop;
3480 var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
3481 var key = node.key;
3482 return typeof key == "string" && key != prop && key != "__proto__";
3483 } : function(node) {
3484 var key = node.key;
3485 if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) {
3486 return typeof key == "string" && key != prop;
3487 }
3488 return key !== "__proto__";
3489 };
3490 if (!all(value.properties, diff)) break;
3491 value.properties.push(make_node(AST_ObjectKeyVal, node, {
3492 key: prop,
3493 value: node.right
3494 }));
3495 exprs.shift();
3496 trimmed = true;
3497 } while (exprs.length);
3498 return trimmed;
3499 }
3500
3501 function join_consecutive_vars(statements) {
3502 var defs;
3503 for (var i = 0, j = -1; i < statements.length; i++) {
3504 var stat = statements[i];
3505 var prev = statements[j];
3506 if (stat instanceof AST_Definitions) {
3507 if (prev && prev.TYPE == stat.TYPE) {
3508 prev.definitions = prev.definitions.concat(stat.definitions);
3509 CHANGED = true;
3510 } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
3511 defs.definitions = defs.definitions.concat(stat.definitions);
3512 CHANGED = true;
3513 } else if (stat instanceof AST_Var) {
3514 var exprs = merge_assigns(prev, stat);
3515 if (exprs) {
3516 if (exprs.length) {
3517 prev.body = make_sequence(prev, exprs);
3518 j++;
3519 }
3520 CHANGED = true;
3521 } else {
3522 j++;
3523 }
3524 statements[j] = defs = stat;
3525 } else {
3526 statements[++j] = stat;
3527 }
3528 continue;
3529 } else if (stat instanceof AST_Exit) {
3530 stat.value = join_assigns_expr(stat.value);
3531 } else if (stat instanceof AST_For) {
3532 var exprs = join_assigns(prev, stat.init);
3533 if (exprs) {
3534 CHANGED = true;
3535 stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
3536 } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
3537 if (stat.init) {
3538 prev.definitions = prev.definitions.concat(stat.init.definitions);
3539 }
3540 defs = stat.init = prev;
3541 statements[j] = merge_defns(stat);
3542 CHANGED = true;
3543 continue;
3544 } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
3545 defs.definitions = defs.definitions.concat(stat.init.definitions);
3546 stat.init = null;
3547 CHANGED = true;
3548 } else if (stat.init instanceof AST_Var) {
3549 defs = stat.init;
3550 exprs = merge_assigns(prev, stat.init);
3551 if (exprs) {
3552 CHANGED = true;
3553 if (exprs.length == 0) {
3554 statements[j] = merge_defns(stat);
3555 continue;
3556 }
3557 prev.body = make_sequence(prev, exprs);
3558 }
3559 }
3560 } else if (stat instanceof AST_ForEnumeration) {
3561 if (defs && defs.TYPE == stat.init.TYPE) {
3562 var defns = defs.definitions.slice();
3563 stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) {
3564 defns.push(make_node(AST_VarDef, name, {
3565 name: name,
3566 value: null
3567 }));
3568 name.definition().references.push(ref);
3569 });
3570 defs.definitions = defns;
3571 CHANGED = true;
3572 }
3573 stat.object = join_assigns_expr(stat.object);
3574 } else if (stat instanceof AST_If) {
3575 stat.condition = join_assigns_expr(stat.condition);
3576 } else if (stat instanceof AST_SimpleStatement) {
3577 var exprs = join_assigns(prev, stat.body);
3578 if (exprs) {
3579 CHANGED = true;
3580 if (!exprs.length) continue;
3581 stat.body = make_sequence(stat.body, exprs);
3582 }
3583 } else if (stat instanceof AST_Switch) {
3584 stat.expression = join_assigns_expr(stat.expression);
3585 } else if (stat instanceof AST_With) {
3586 stat.expression = join_assigns_expr(stat.expression);
3587 }
3588 statements[++j] = defs ? merge_defns(stat) : stat;
3589 }
3590 statements.length = j + 1;
3591
3592 function join_assigns_expr(value) {
3593 var exprs = join_assigns(prev, value, 1);
3594 if (!exprs) return value;
3595 CHANGED = true;
3596 var tail = value.tail_node();
3597 if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left);
3598 return make_sequence(value, exprs);
3599 }
3600
3601 function merge_defns(stat) {
3602 return stat.transform(new TreeTransformer(function(node, descend, in_list) {
3603 if (node instanceof AST_Definitions) {
3604 if (defs === node) return node;
3605 if (defs.TYPE != node.TYPE) return node;
3606 var parent = this.parent();
3607 if (parent instanceof AST_ForEnumeration && parent.init === node) return node;
3608 if (!declarations_only(node)) return node;
3609 defs.definitions = defs.definitions.concat(node.definitions);
3610 CHANGED = true;
3611 if (parent instanceof AST_For && parent.init === node) return null;
3612 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
3613 }
3614 if (node instanceof AST_ExportDeclaration) return node;
3615 if (node instanceof AST_Scope) return node;
3616 if (!is_statement(node)) return node;
3617 }));
3618 }
3619 }
3620 }
3621
3622 function extract_declarations_from_unreachable_code(compressor, stat, target) {
3623 if (!(stat instanceof AST_Definitions || stat instanceof AST_LambdaDefinition)) {
3624 AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
3625 }
3626 var block;
3627 stat.walk(new TreeWalker(function(node, descend) {
3628 if (node instanceof AST_Definitions) {
3629 var defns = [];
3630 if (node.remove_initializers(compressor, defns)) {
3631 AST_Node.warn("Dropping initialization in unreachable code [{file}:{line},{col}]", node.start);
3632 }
3633 if (defns.length > 0) {
3634 node.definitions = defns;
3635 push(node);
3636 }
3637 return true;
3638 }
3639 if (node instanceof AST_LambdaDefinition) {
3640 push(node);
3641 return true;
3642 }
3643 if (node instanceof AST_Scope) return true;
3644 if (node instanceof AST_BlockScope) {
3645 var save = block;
3646 block = [];
3647 descend();
3648 if (block.required) {
3649 target.push(make_node(AST_BlockStatement, stat, {
3650 body: block
3651 }));
3652 } else if (block.length) {
3653 [].push.apply(target, block);
3654 }
3655 block = save;
3656 return true;
3657 }
3658 }));
3659 function push(node) {
3660 if (block) {
3661 block.push(node);
3662 if (!safe_to_trim(node)) block.required = true;
3663 } else {
3664 target.push(node);
3665 }
3666 }
3667 }
3668
3669 function is_undefined(node, compressor) {
3670 return node.is_undefined
3671 || node instanceof AST_Undefined
3672 || node instanceof AST_UnaryPrefix
3673 && node.operator == "void"
3674 && !(compressor && node.expression.has_side_effects(compressor));
3675 }
3676
3677 // is_truthy()
3678 // return true if `!!node === true`
3679 (function(def) {
3680 def(AST_Node, return_false);
3681 def(AST_Array, return_true);
3682 def(AST_Assign, function() {
3683 return this.operator == "=" && this.right.is_truthy();
3684 });
3685 def(AST_Lambda, return_true);
3686 def(AST_Object, return_true);
3687 def(AST_RegExp, return_true);
3688 def(AST_Sequence, function() {
3689 return this.tail_node().is_truthy();
3690 });
3691 def(AST_SymbolRef, function() {
3692 var fixed = this.fixed_value();
3693 if (!fixed) return false;
3694 this.is_truthy = return_false;
3695 var result = fixed.is_truthy();
3696 delete this.is_truthy;
3697 return result;
3698 });
3699 })(function(node, func) {
3700 node.DEFMETHOD("is_truthy", func);
3701 });
3702
3703 // is_negative_zero()
3704 // return true if the node may represent -0
3705 (function(def) {
3706 def(AST_Node, return_true);
3707 def(AST_Array, return_false);
3708 function binary(op, left, right) {
3709 switch (op) {
3710 case "-":
3711 return left.is_negative_zero()
3712 && (!(right instanceof AST_Constant) || right.value == 0);
3713 case "&&":
3714 case "||":
3715 return left.is_negative_zero() || right.is_negative_zero();
3716 case "*":
3717 case "/":
3718 case "%":
3719 case "**":
3720 return true;
3721 default:
3722 return false;
3723 }
3724 }
3725 def(AST_Assign, function() {
3726 var op = this.operator;
3727 if (op == "=") return this.right.is_negative_zero();
3728 return binary(op.slice(0, -1), this.left, this.right);
3729 });
3730 def(AST_Binary, function() {
3731 return binary(this.operator, this.left, this.right);
3732 });
3733 def(AST_Constant, function() {
3734 return this.value == 0 && 1 / this.value < 0;
3735 });
3736 def(AST_Lambda, return_false);
3737 def(AST_Object, return_false);
3738 def(AST_RegExp, return_false);
3739 def(AST_Sequence, function() {
3740 return this.tail_node().is_negative_zero();
3741 });
3742 def(AST_SymbolRef, function() {
3743 var fixed = this.fixed_value();
3744 if (!fixed) return true;
3745 this.is_negative_zero = return_true;
3746 var result = fixed.is_negative_zero();
3747 delete this.is_negative_zero;
3748 return result;
3749 });
3750 def(AST_UnaryPrefix, function() {
3751 return this.operator == "+" && this.expression.is_negative_zero()
3752 || this.operator == "-";
3753 });
3754 })(function(node, func) {
3755 node.DEFMETHOD("is_negative_zero", func);
3756 });
3757
3758 // may_throw_on_access()
3759 // returns true if this node may be null, undefined or contain `AST_Accessor`
3760 (function(def) {
3761 AST_Node.DEFMETHOD("may_throw_on_access", function(compressor, force) {
3762 return !compressor.option("pure_getters") || this._dot_throw(compressor, force);
3763 });
3764 function is_strict(compressor, force) {
3765 return force || /strict/.test(compressor.option("pure_getters"));
3766 }
3767 def(AST_Node, is_strict);
3768 def(AST_Array, return_false);
3769 def(AST_Assign, function(compressor) {
3770 var op = this.operator;
3771 var sym = this.left;
3772 var rhs = this.right;
3773 if (op != "=") {
3774 return lazy_op[op.slice(0, -1)] && (sym._dot_throw(compressor) || rhs._dot_throw(compressor));
3775 }
3776 if (!rhs._dot_throw(compressor)) return false;
3777 if (!(sym instanceof AST_SymbolRef)) return true;
3778 if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
3779 return rhs.right._dot_throw(compressor);
3780 }
3781 return true;
3782 });
3783 def(AST_Binary, function(compressor) {
3784 return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
3785 });
3786 def(AST_Class, return_false);
3787 def(AST_Conditional, function(compressor) {
3788 return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
3789 });
3790 def(AST_Constant, return_false);
3791 def(AST_Dot, function(compressor, force) {
3792 if (!is_strict(compressor, force)) return false;
3793 var exp = this.expression;
3794 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
3795 return !(this.property == "prototype" && is_lambda(exp));
3796 });
3797 def(AST_Lambda, return_false);
3798 def(AST_Null, return_true);
3799 def(AST_Object, function(compressor, force) {
3800 return is_strict(compressor, force) && !all(this.properties, function(prop) {
3801 if (!(prop instanceof AST_ObjectKeyVal)) return false;
3802 return !(prop.key == "__proto__" && prop.value._dot_throw(compressor, force));
3803 });
3804 });
3805 def(AST_ObjectIdentity, function(compressor, force) {
3806 return is_strict(compressor, force) && !this.scope.new;
3807 });
3808 def(AST_Sequence, function(compressor) {
3809 return this.tail_node()._dot_throw(compressor);
3810 });
3811 def(AST_SymbolRef, function(compressor, force) {
3812 if (this.is_undefined) return true;
3813 if (!is_strict(compressor, force)) return false;
3814 if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
3815 if (this.is_immutable()) return false;
3816 var def = this.definition();
3817 if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) {
3818 return argname instanceof AST_SymbolFunarg;
3819 })) return def.scope.uses_arguments > 2;
3820 var fixed = this.fixed_value();
3821 if (!fixed) return true;
3822 this._dot_throw = return_true;
3823 if (fixed._dot_throw(compressor)) {
3824 delete this._dot_throw;
3825 return true;
3826 }
3827 this._dot_throw = return_false;
3828 return false;
3829 });
3830 def(AST_UnaryPrefix, function() {
3831 return this.operator == "void";
3832 });
3833 def(AST_UnaryPostfix, return_false);
3834 def(AST_Undefined, return_true);
3835 })(function(node, func) {
3836 node.DEFMETHOD("_dot_throw", func);
3837 });
3838
3839 (function(def) {
3840 def(AST_Node, return_false);
3841 def(AST_Array, return_true);
3842 function is_binary_defined(compressor, op, node) {
3843 switch (op) {
3844 case "&&":
3845 return node.left.is_defined(compressor) && node.right.is_defined(compressor);
3846 case "||":
3847 return node.left.is_truthy() || node.right.is_defined(compressor);
3848 case "??":
3849 return node.left.is_defined(compressor) || node.right.is_defined(compressor);
3850 default:
3851 return true;
3852 }
3853 }
3854 def(AST_Assign, function(compressor) {
3855 var op = this.operator;
3856 if (op == "=") return this.right.is_defined(compressor);
3857 return is_binary_defined(compressor, op.slice(0, -1), this);
3858 });
3859 def(AST_Binary, function(compressor) {
3860 return is_binary_defined(compressor, this.operator, this);
3861 });
3862 def(AST_Conditional, function(compressor) {
3863 return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
3864 });
3865 def(AST_Constant, return_true);
3866 def(AST_Hole, return_false);
3867 def(AST_Lambda, return_true);
3868 def(AST_Object, return_true);
3869 def(AST_Sequence, function(compressor) {
3870 return this.tail_node().is_defined(compressor);
3871 });
3872 def(AST_SymbolRef, function(compressor) {
3873 if (this.is_undefined) return false;
3874 if (is_undeclared_ref(this) && this.is_declared(compressor)) return true;
3875 if (this.is_immutable()) return true;
3876 var fixed = this.fixed_value();
3877 if (!fixed) return false;
3878 this.is_defined = return_false;
3879 var result = fixed.is_defined(compressor);
3880 delete this.is_defined;
3881 return result;
3882 });
3883 def(AST_UnaryPrefix, function() {
3884 return this.operator != "void";
3885 });
3886 def(AST_UnaryPostfix, return_true);
3887 def(AST_Undefined, return_false);
3888 })(function(node, func) {
3889 node.DEFMETHOD("is_defined", func);
3890 });
3891
3892 /* -----[ boolean/negation helpers ]----- */
3893
3894 // methods to determine whether an expression has a boolean result type
3895 (function(def) {
3896 def(AST_Node, return_false);
3897 def(AST_Assign, function(compressor) {
3898 return this.operator == "=" && this.right.is_boolean(compressor);
3899 });
3900 var binary = makePredicate("in instanceof == != === !== < <= >= >");
3901 def(AST_Binary, function(compressor) {
3902 return binary[this.operator] || lazy_op[this.operator]
3903 && this.left.is_boolean(compressor)
3904 && this.right.is_boolean(compressor);
3905 });
3906 def(AST_Boolean, return_true);
3907 var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some");
3908 def(AST_Call, function(compressor) {
3909 if (!compressor.option("unsafe")) return false;
3910 var exp = this.expression;
3911 return exp instanceof AST_Dot && (fn[exp.property]
3912 || exp.property == "test" && exp.expression instanceof AST_RegExp);
3913 });
3914 def(AST_Conditional, function(compressor) {
3915 return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
3916 });
3917 def(AST_New, return_false);
3918 def(AST_Sequence, function(compressor) {
3919 return this.tail_node().is_boolean(compressor);
3920 });
3921 def(AST_SymbolRef, function(compressor) {
3922 var fixed = this.fixed_value();
3923 if (!fixed) return false;
3924 this.is_boolean = return_false;
3925 var result = fixed.is_boolean(compressor);
3926 delete this.is_boolean;
3927 return result;
3928 });
3929 var unary = makePredicate("! delete");
3930 def(AST_UnaryPrefix, function() {
3931 return unary[this.operator];
3932 });
3933 })(function(node, func) {
3934 node.DEFMETHOD("is_boolean", func);
3935 });
3936
3937 // methods to determine if an expression has a numeric result type
3938 (function(def) {
3939 def(AST_Node, return_false);
3940 var binary = makePredicate("- * / % ** & | ^ << >> >>>");
3941 def(AST_Assign, function(compressor) {
3942 return binary[this.operator.slice(0, -1)]
3943 || this.operator == "=" && this.right.is_number(compressor);
3944 });
3945 def(AST_Binary, function(compressor) {
3946 if (binary[this.operator]) return true;
3947 if (this.operator != "+") return false;
3948 return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
3949 && (this.right.is_boolean(compressor) || this.right.is_number(compressor));
3950 });
3951 var fn = makePredicate([
3952 "charCodeAt",
3953 "getDate",
3954 "getDay",
3955 "getFullYear",
3956 "getHours",
3957 "getMilliseconds",
3958 "getMinutes",
3959 "getMonth",
3960 "getSeconds",
3961 "getTime",
3962 "getTimezoneOffset",
3963 "getUTCDate",
3964 "getUTCDay",
3965 "getUTCFullYear",
3966 "getUTCHours",
3967 "getUTCMilliseconds",
3968 "getUTCMinutes",
3969 "getUTCMonth",
3970 "getUTCSeconds",
3971 "getYear",
3972 "indexOf",
3973 "lastIndexOf",
3974 "localeCompare",
3975 "push",
3976 "search",
3977 "setDate",
3978 "setFullYear",
3979 "setHours",
3980 "setMilliseconds",
3981 "setMinutes",
3982 "setMonth",
3983 "setSeconds",
3984 "setTime",
3985 "setUTCDate",
3986 "setUTCFullYear",
3987 "setUTCHours",
3988 "setUTCMilliseconds",
3989 "setUTCMinutes",
3990 "setUTCMonth",
3991 "setUTCSeconds",
3992 "setYear",
3993 "toExponential",
3994 "toFixed",
3995 "toPrecision",
3996 ]);
3997 def(AST_Call, function(compressor) {
3998 if (!compressor.option("unsafe")) return false;
3999 var exp = this.expression;
4000 return exp instanceof AST_Dot && (fn[exp.property]
4001 || is_undeclared_ref(exp.expression) && exp.expression.name == "Math");
4002 });
4003 def(AST_Conditional, function(compressor) {
4004 return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
4005 });
4006 def(AST_New, return_false);
4007 def(AST_Number, return_true);
4008 def(AST_Sequence, function(compressor) {
4009 return this.tail_node().is_number(compressor);
4010 });
4011 def(AST_SymbolRef, function(compressor) {
4012 var fixed = this.fixed_value();
4013 if (!fixed) return false;
4014 this.is_number = return_false;
4015 var result = fixed.is_number(compressor);
4016 delete this.is_number;
4017 return result;
4018 });
4019 var unary = makePredicate("+ - ~ ++ --");
4020 def(AST_Unary, function() {
4021 return unary[this.operator];
4022 });
4023 })(function(node, func) {
4024 node.DEFMETHOD("is_number", func);
4025 });
4026
4027 // methods to determine if an expression has a string result type
4028 (function(def) {
4029 def(AST_Node, return_false);
4030 def(AST_Assign, function(compressor) {
4031 switch (this.operator) {
4032 case "+=":
4033 if (this.left.is_string(compressor)) return true;
4034 case "=":
4035 return this.right.is_string(compressor);
4036 }
4037 });
4038 def(AST_Binary, function(compressor) {
4039 return this.operator == "+" &&
4040 (this.left.is_string(compressor) || this.right.is_string(compressor));
4041 });
4042 var fn = makePredicate([
4043 "charAt",
4044 "substr",
4045 "substring",
4046 "toLowerCase",
4047 "toString",
4048 "toUpperCase",
4049 "trim",
4050 ]);
4051 def(AST_Call, function(compressor) {
4052 if (!compressor.option("unsafe")) return false;
4053 var exp = this.expression;
4054 return exp instanceof AST_Dot && fn[exp.property];
4055 });
4056 def(AST_Conditional, function(compressor) {
4057 return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
4058 });
4059 def(AST_Sequence, function(compressor) {
4060 return this.tail_node().is_string(compressor);
4061 });
4062 def(AST_String, return_true);
4063 def(AST_SymbolRef, function(compressor) {
4064 var fixed = this.fixed_value();
4065 if (!fixed) return false;
4066 this.is_string = return_false;
4067 var result = fixed.is_string(compressor);
4068 delete this.is_string;
4069 return result;
4070 });
4071 def(AST_Template, function(compressor) {
4072 return !this.tag || is_raw_tag(compressor, this.tag);
4073 });
4074 def(AST_UnaryPrefix, function() {
4075 return this.operator == "typeof";
4076 });
4077 })(function(node, func) {
4078 node.DEFMETHOD("is_string", func);
4079 });
4080
4081 var lazy_op = makePredicate("&& || ??");
4082
4083 (function(def) {
4084 function to_node(value, orig) {
4085 if (value instanceof AST_Node) return value.clone(true);
4086 if (Array.isArray(value)) return make_node(AST_Array, orig, {
4087 elements: value.map(function(value) {
4088 return to_node(value, orig);
4089 })
4090 });
4091 if (value && typeof value == "object") {
4092 var props = [];
4093 for (var key in value) if (HOP(value, key)) {
4094 props.push(make_node(AST_ObjectKeyVal, orig, {
4095 key: key,
4096 value: to_node(value[key], orig)
4097 }));
4098 }
4099 return make_node(AST_Object, orig, {
4100 properties: props
4101 });
4102 }
4103 return make_node_from_constant(value, orig);
4104 }
4105
4106 function warn(node) {
4107 AST_Node.warn("global_defs {node} redefined [{file}:{line},{col}]", {
4108 node: node,
4109 file: node.start.file,
4110 line: node.start.line,
4111 col: node.start.col,
4112 });
4113 }
4114
4115 AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
4116 if (!compressor.option("global_defs")) return this;
4117 this.figure_out_scope({ ie8: compressor.option("ie8") });
4118 return this.transform(new TreeTransformer(function(node) {
4119 var def = node._find_defs(compressor, "");
4120 if (!def) return;
4121 var level = 0, child = node, parent;
4122 while (parent = this.parent(level++)) {
4123 if (!(parent instanceof AST_PropAccess)) break;
4124 if (parent.expression !== child) break;
4125 child = parent;
4126 }
4127 if (is_lhs(child, parent)) {
4128 warn(node);
4129 return;
4130 }
4131 return def;
4132 }));
4133 });
4134 def(AST_Node, noop);
4135 def(AST_Dot, function(compressor, suffix) {
4136 return this.expression._find_defs(compressor, "." + this.property + suffix);
4137 });
4138 def(AST_SymbolDeclaration, function(compressor) {
4139 if (!this.definition().global) return;
4140 if (HOP(compressor.option("global_defs"), this.name)) warn(this);
4141 });
4142 def(AST_SymbolRef, function(compressor, suffix) {
4143 if (!this.definition().global) return;
4144 var defines = compressor.option("global_defs");
4145 var name = this.name + suffix;
4146 if (HOP(defines, name)) return to_node(defines[name], this);
4147 });
4148 })(function(node, func) {
4149 node.DEFMETHOD("_find_defs", func);
4150 });
4151
4152 function best_of_expression(ast1, ast2, threshold) {
4153 var delta = ast2.print_to_string().length - ast1.print_to_string().length;
4154 return delta < (threshold || 0) ? ast2 : ast1;
4155 }
4156
4157 function best_of_statement(ast1, ast2, threshold) {
4158 return best_of_expression(make_node(AST_SimpleStatement, ast1, {
4159 body: ast1
4160 }), make_node(AST_SimpleStatement, ast2, {
4161 body: ast2
4162 }), threshold).body;
4163 }
4164
4165 function best_of(compressor, ast1, ast2, threshold) {
4166 return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
4167 }
4168
4169 function convert_to_predicate(obj) {
4170 var map = Object.create(null);
4171 Object.keys(obj).forEach(function(key) {
4172 map[key] = makePredicate(obj[key]);
4173 });
4174 return map;
4175 }
4176
4177 function skip_directives(body) {
4178 for (var i = 0; i < body.length; i++) {
4179 var stat = body[i];
4180 if (!(stat instanceof AST_Directive)) return stat;
4181 }
4182 }
4183
4184 function arrow_first_statement() {
4185 if (this.value) return make_node(AST_Return, this.value, {
4186 value: this.value
4187 });
4188 return skip_directives(this.body);
4189 }
4190 AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement);
4191 AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement);
4192 AST_Lambda.DEFMETHOD("first_statement", function() {
4193 return skip_directives(this.body);
4194 });
4195
4196 AST_Lambda.DEFMETHOD("length", function() {
4197 var argnames = this.argnames;
4198 for (var i = 0; i < argnames.length; i++) {
4199 if (argnames[i] instanceof AST_DefaultValue) break;
4200 }
4201 return i;
4202 });
4203
4204 function try_evaluate(compressor, node) {
4205 var ev = node.evaluate(compressor);
4206 if (ev === node) return node;
4207 ev = make_node_from_constant(ev, node).optimize(compressor);
4208 return best_of(compressor, node, ev, compressor.eval_threshold);
4209 }
4210
4211 var object_fns = [
4212 "constructor",
4213 "toString",
4214 "valueOf",
4215 ];
4216 var native_fns = convert_to_predicate({
4217 Array: [
4218 "indexOf",
4219 "join",
4220 "lastIndexOf",
4221 "slice",
4222 ].concat(object_fns),
4223 Boolean: object_fns,
4224 Function: object_fns,
4225 Number: [
4226 "toExponential",
4227 "toFixed",
4228 "toPrecision",
4229 ].concat(object_fns),
4230 Object: object_fns,
4231 RegExp: [
4232 "exec",
4233 "test",
4234 ].concat(object_fns),
4235 String: [
4236 "charAt",
4237 "charCodeAt",
4238 "concat",
4239 "indexOf",
4240 "italics",
4241 "lastIndexOf",
4242 "match",
4243 "replace",
4244 "search",
4245 "slice",
4246 "split",
4247 "substr",
4248 "substring",
4249 "toLowerCase",
4250 "toUpperCase",
4251 "trim",
4252 ].concat(object_fns),
4253 });
4254 var static_fns = convert_to_predicate({
4255 Array: [
4256 "isArray",
4257 ],
4258 Math: [
4259 "abs",
4260 "acos",
4261 "asin",
4262 "atan",
4263 "ceil",
4264 "cos",
4265 "exp",
4266 "floor",
4267 "log",
4268 "round",
4269 "sin",
4270 "sqrt",
4271 "tan",
4272 "atan2",
4273 "pow",
4274 "max",
4275 "min",
4276 ],
4277 Number: [
4278 "isFinite",
4279 "isNaN",
4280 ],
4281 Object: [
4282 "create",
4283 "getOwnPropertyDescriptor",
4284 "getOwnPropertyNames",
4285 "getPrototypeOf",
4286 "isExtensible",
4287 "isFrozen",
4288 "isSealed",
4289 "keys",
4290 ],
4291 String: [
4292 "fromCharCode",
4293 "raw",
4294 ],
4295 });
4296
4297 // Accomodate when compress option evaluate=false
4298 // as well as the common constant expressions !0 and -1
4299 (function(def) {
4300 def(AST_Node, return_false);
4301 def(AST_Constant, return_true);
4302 def(AST_RegExp, return_false);
4303 var unaryPrefix = makePredicate("! ~ - + void");
4304 def(AST_UnaryPrefix, function() {
4305 return unaryPrefix[this.operator] && this.expression instanceof AST_Constant;
4306 });
4307 })(function(node, func) {
4308 node.DEFMETHOD("is_constant", func);
4309 });
4310
4311 // methods to evaluate a constant expression
4312 (function(def) {
4313 // If the node has been successfully reduced to a constant,
4314 // then its value is returned; otherwise the element itself
4315 // is returned.
4316 //
4317 // They can be distinguished as constant value is never a
4318 // descendant of AST_Node.
4319 //
4320 // When `ignore_side_effects` is `true`, inspect the constant value
4321 // produced without worrying about any side effects caused by said
4322 // expression.
4323 AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
4324 if (!compressor.option("evaluate")) return this;
4325 var cached = [];
4326 var val = this._eval(compressor, ignore_side_effects, cached, 1);
4327 cached.forEach(function(node) {
4328 delete node._eval;
4329 });
4330 if (ignore_side_effects) return val;
4331 if (!val || val instanceof RegExp) return val;
4332 if (typeof val == "function" || typeof val == "object") return this;
4333 return val;
4334 });
4335 var scan_modified = new TreeWalker(function(node) {
4336 if (node instanceof AST_Assign) modified(node.left);
4337 if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression);
4338 });
4339 function modified(node) {
4340 if (node instanceof AST_DestructuredArray) {
4341 node.elements.forEach(modified);
4342 } else if (node instanceof AST_DestructuredObject) {
4343 node.properties.forEach(function(prop) {
4344 modified(prop.value);
4345 });
4346 } else if (node instanceof AST_PropAccess) {
4347 modified(node.expression);
4348 } else if (node instanceof AST_SymbolRef) {
4349 node.definition().references.forEach(function(ref) {
4350 delete ref._eval;
4351 });
4352 }
4353 }
4354 def(AST_Statement, function() {
4355 throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
4356 });
4357 def(AST_Accessor, return_this);
4358 def(AST_BigInt, return_this);
4359 def(AST_Class, return_this);
4360 def(AST_Node, return_this);
4361 def(AST_Constant, function() {
4362 return this.value;
4363 });
4364 def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
4365 var lhs = this.left;
4366 if (!ignore_side_effects) {
4367 if (!(lhs instanceof AST_SymbolRef)) return this;
4368 if (!HOP(lhs, "_eval")) {
4369 if (!lhs.fixed) return this;
4370 var def = lhs.definition();
4371 if (!def.fixed) return this;
4372 if (def.undeclared) return this;
4373 if (def.last_ref !== lhs) return this;
4374 if (def.single_use == "m") return this;
4375 }
4376 }
4377 var op = this.operator;
4378 var node;
4379 if (!HOP(lhs, "_eval") && lhs instanceof AST_SymbolRef && lhs.fixed && lhs.definition().fixed) {
4380 node = lhs;
4381 } else if (op == "=") {
4382 node = this.right;
4383 } else {
4384 node = make_node(AST_Binary, this, {
4385 operator: op.slice(0, -1),
4386 left: lhs,
4387 right: this.right,
4388 });
4389 }
4390 lhs.walk(scan_modified);
4391 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4392 if (typeof value == "object") return this;
4393 modified(lhs);
4394 return value;
4395 });
4396 def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
4397 if (!ignore_side_effects) return this;
4398 var exprs = this.expressions;
4399 for (var i = 0, last = exprs.length - 1; i < last; i++) {
4400 exprs[i].walk(scan_modified);
4401 }
4402 var tail = exprs[last];
4403 var value = tail._eval(compressor, ignore_side_effects, cached, depth);
4404 return value === tail ? this : value;
4405 });
4406 def(AST_Lambda, function(compressor) {
4407 if (compressor.option("unsafe")) {
4408 var fn = function() {};
4409 fn.node = this;
4410 fn.toString = function() {
4411 return "function(){}";
4412 };
4413 return fn;
4414 }
4415 return this;
4416 });
4417 def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
4418 if (compressor.option("unsafe")) {
4419 var elements = [];
4420 for (var i = 0; i < this.elements.length; i++) {
4421 var element = this.elements[i];
4422 if (element instanceof AST_Hole) return this;
4423 var value = element._eval(compressor, ignore_side_effects, cached, depth);
4424 if (element === value) return this;
4425 elements.push(value);
4426 }
4427 return elements;
4428 }
4429 return this;
4430 });
4431 var nonsafe_props = makePredicate("__proto__ toString valueOf");
4432 def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
4433 if (compressor.option("unsafe")) {
4434 var val = {};
4435 for (var i = 0; i < this.properties.length; i++) {
4436 var prop = this.properties[i];
4437 if (!(prop instanceof AST_ObjectKeyVal)) return this;
4438 var key = prop.key;
4439 if (key instanceof AST_Node) {
4440 key = key._eval(compressor, ignore_side_effects, cached, depth);
4441 if (key === prop.key) return this;
4442 }
4443 if (nonsafe_props[key]) return this;
4444 val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
4445 if (val[key] === prop.value) return this;
4446 }
4447 return val;
4448 }
4449 return this;
4450 });
4451 var non_converting_unary = makePredicate("! typeof void");
4452 def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
4453 var e = this.expression;
4454 var op = this.operator;
4455 // Function would be evaluated to an array and so typeof would
4456 // incorrectly return "object". Hence making is a special case.
4457 if (compressor.option("typeofs")
4458 && op == "typeof"
4459 && (e instanceof AST_Lambda
4460 || e instanceof AST_SymbolRef
4461 && e.fixed_value() instanceof AST_Lambda)) {
4462 return typeof function(){};
4463 }
4464 var def = e instanceof AST_SymbolRef && e.definition();
4465 if (!non_converting_unary[op] && !(def && def.fixed)) depth++;
4466 e.walk(scan_modified);
4467 var v = e._eval(compressor, ignore_side_effects, cached, depth);
4468 if (v === e) {
4469 if (ignore_side_effects && op == "void") return;
4470 return this;
4471 }
4472 switch (op) {
4473 case "!": return !v;
4474 case "typeof":
4475 // typeof <RegExp> returns "object" or "function" on different platforms
4476 // so cannot evaluate reliably
4477 if (v instanceof RegExp) return this;
4478 return typeof v;
4479 case "void": return;
4480 case "~": return ~v;
4481 case "-": return -v;
4482 case "+": return +v;
4483 case "++":
4484 case "--":
4485 if (!def) return this;
4486 if (!ignore_side_effects) {
4487 if (def.undeclared) return this;
4488 if (def.last_ref !== e) return this;
4489 }
4490 if (HOP(e, "_eval")) v = +(op[0] + 1) + +v;
4491 modified(e);
4492 return v;
4493 }
4494 return this;
4495 });
4496 def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) {
4497 var e = this.expression;
4498 if (!(e instanceof AST_SymbolRef)) {
4499 if (!ignore_side_effects) return this;
4500 } else if (!HOP(e, "_eval")) {
4501 if (!e.fixed) return this;
4502 if (!ignore_side_effects) {
4503 var def = e.definition();
4504 if (!def.fixed) return this;
4505 if (def.undeclared) return this;
4506 if (def.last_ref !== e) return this;
4507 }
4508 }
4509 if (!(e instanceof AST_SymbolRef && e.definition().fixed)) depth++;
4510 e.walk(scan_modified);
4511 var v = e._eval(compressor, ignore_side_effects, cached, depth);
4512 if (v === e) return this;
4513 modified(e);
4514 return +v;
4515 });
4516 var non_converting_binary = makePredicate("&& || === !==");
4517 def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
4518 if (!non_converting_binary[this.operator]) depth++;
4519 var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
4520 if (left === this.left) return this;
4521 if (this.operator == (left ? "||" : "&&")) return left;
4522 var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object");
4523 var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth);
4524 if (right === this.right) return this;
4525 var result;
4526 switch (this.operator) {
4527 case "&&" : result = left && right; break;
4528 case "||" : result = left || right; break;
4529 case "??" :
4530 result = left == null ? right : left;
4531 break;
4532 case "|" : result = left | right; break;
4533 case "&" : result = left & right; break;
4534 case "^" : result = left ^ right; break;
4535 case "+" : result = left + right; break;
4536 case "-" : result = left - right; break;
4537 case "*" : result = left * right; break;
4538 case "/" : result = left / right; break;
4539 case "%" : result = left % right; break;
4540 case "<<" : result = left << right; break;
4541 case ">>" : result = left >> right; break;
4542 case ">>>": result = left >>> right; break;
4543 case "==" : result = left == right; break;
4544 case "===": result = left === right; break;
4545 case "!=" : result = left != right; break;
4546 case "!==": result = left !== right; break;
4547 case "<" : result = left < right; break;
4548 case "<=" : result = left <= right; break;
4549 case ">" : result = left > right; break;
4550 case ">=" : result = left >= right; break;
4551 case "**":
4552 result = Math.pow(left, right);
4553 break;
4554 case "in":
4555 if (right && typeof right == "object" && HOP(right, left)) {
4556 result = true;
4557 break;
4558 }
4559 default:
4560 return this;
4561 }
4562 if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
4563 if (compressor.option("unsafe_math")
4564 && !ignore_side_effects
4565 && result
4566 && typeof result == "number"
4567 && (this.operator == "+" || this.operator == "-")) {
4568 var digits = Math.max(0, decimals(left), decimals(right));
4569 // 53-bit significand ---> 15.95 decimal places
4570 if (digits < 16) return +result.toFixed(digits);
4571 }
4572 return result;
4573
4574 function decimals(operand) {
4575 var match = /(\.[0-9]*)?(e.+)?$/.exec(+operand);
4576 return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
4577 }
4578 });
4579 def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
4580 var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
4581 if (condition === this.condition) return this;
4582 var node = condition ? this.consequent : this.alternative;
4583 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4584 return value === node ? this : value;
4585 });
4586 function verify_escaped(ref, depth) {
4587 var escaped = ref.definition().escaped;
4588 switch (escaped.length) {
4589 case 0:
4590 return true;
4591 case 1:
4592 var found = false;
4593 escaped[0].walk(new TreeWalker(function(node) {
4594 if (found) return true;
4595 if (node === ref) return found = true;
4596 if (node instanceof AST_Scope) return true;
4597 }));
4598 return found;
4599 default:
4600 return depth <= escaped.depth;
4601 }
4602 }
4603 def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
4604 var fixed = this.fixed_value();
4605 if (!fixed) return this;
4606 var value;
4607 if (HOP(fixed, "_eval")) {
4608 value = fixed._eval();
4609 } else {
4610 this._eval = return_this;
4611 value = fixed._eval(compressor, ignore_side_effects, cached, depth);
4612 delete this._eval;
4613 if (value === fixed) return this;
4614 fixed._eval = function() {
4615 return value;
4616 };
4617 cached.push(fixed);
4618 }
4619 return value && typeof value == "object" && !verify_escaped(this, depth) ? this : value;
4620 });
4621 var global_objs = {
4622 Array: Array,
4623 Math: Math,
4624 Number: Number,
4625 Object: Object,
4626 String: String,
4627 };
4628 var static_values = convert_to_predicate({
4629 Math: [
4630 "E",
4631 "LN10",
4632 "LN2",
4633 "LOG2E",
4634 "LOG10E",
4635 "PI",
4636 "SQRT1_2",
4637 "SQRT2",
4638 ],
4639 Number: [
4640 "MAX_VALUE",
4641 "MIN_VALUE",
4642 "NaN",
4643 "NEGATIVE_INFINITY",
4644 "POSITIVE_INFINITY",
4645 ],
4646 });
4647 var regexp_props = makePredicate("global ignoreCase multiline source");
4648 def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
4649 if (compressor.option("unsafe")) {
4650 var val;
4651 var exp = this.expression;
4652 if (!is_undeclared_ref(exp)) {
4653 val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
4654 if (val == null || val === exp) return this;
4655 }
4656 var key = this.property;
4657 if (key instanceof AST_Node) {
4658 key = key._eval(compressor, ignore_side_effects, cached, depth);
4659 if (key === this.property) return this;
4660 }
4661 if (val === undefined) {
4662 var static_value = static_values[exp.name];
4663 if (!static_value || !static_value[key]) return this;
4664 val = global_objs[exp.name];
4665 } else if (val instanceof RegExp) {
4666 if (!regexp_props[key]) return this;
4667 } else if (typeof val == "object") {
4668 if (!HOP(val, key)) return this;
4669 } else if (typeof val == "function") switch (key) {
4670 case "name":
4671 return val.node.name ? val.node.name.name : "";
4672 case "length":
4673 return val.node.length();
4674 default:
4675 return this;
4676 }
4677 return val[key];
4678 }
4679 return this;
4680 });
4681 function eval_all(nodes, compressor, ignore_side_effects, cached, depth) {
4682 var values = [];
4683 for (var i = 0; i < nodes.length; i++) {
4684 var node = nodes[i];
4685 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4686 if (node === value) return;
4687 values.push(value);
4688 }
4689 return values;
4690 }
4691 def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
4692 var exp = this.expression;
4693 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
4694 if (fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function) {
4695 if (fn.evaluating) return this;
4696 if (fn.name && fn.name.definition().recursive_refs > 0) return this;
4697 if (this.is_expr_pure(compressor)) return this;
4698 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4699 if (!all(fn.argnames, function(sym, index) {
4700 if (sym instanceof AST_DefaultValue) {
4701 if (!args) return false;
4702 if (args[index] !== undefined) return false;
4703 var value = sym.value._eval(compressor, ignore_side_effects, cached, depth);
4704 if (value === sym.value) return false;
4705 args[index] = value;
4706 sym = sym.name;
4707 }
4708 return !(sym instanceof AST_Destructured);
4709 })) return this;
4710 if (fn.rest instanceof AST_Destructured) return this;
4711 if (!args && !ignore_side_effects) return this;
4712 var stat = fn.first_statement();
4713 if (!(stat instanceof AST_Return)) {
4714 if (ignore_side_effects) {
4715 fn.walk(scan_modified);
4716 var found = false;
4717 fn.evaluating = true;
4718 walk_body(fn, new TreeWalker(function(node) {
4719 if (found) return true;
4720 if (node instanceof AST_Return) {
4721 if (node.value && node.value._eval(compressor, true, cached, depth) !== undefined) {
4722 found = true;
4723 }
4724 return true;
4725 }
4726 if (node instanceof AST_Scope && node !== fn) return true;
4727 }));
4728 delete fn.evaluating;
4729 if (!found) return;
4730 }
4731 return this;
4732 }
4733 var val = stat.value;
4734 if (!val) return;
4735 var cached_args = [];
4736 if (!args || all(fn.argnames, function(sym, i) {
4737 return assign(sym, args[i]);
4738 }) && !(fn.rest && !assign(fn.rest, args.slice(fn.argnames.length))) || ignore_side_effects) {
4739 fn.evaluating = true;
4740 val = val._eval(compressor, ignore_side_effects, cached, depth);
4741 delete fn.evaluating;
4742 }
4743 cached_args.forEach(function(node) {
4744 delete node._eval;
4745 });
4746 return val === stat.value ? this : val;
4747 } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
4748 var key = exp.property;
4749 if (key instanceof AST_Node) {
4750 key = key._eval(compressor, ignore_side_effects, cached, depth);
4751 if (key === exp.property) return this;
4752 }
4753 var val;
4754 var e = exp.expression;
4755 if (is_undeclared_ref(e)) {
4756 var static_fn = static_fns[e.name];
4757 if (!static_fn || !static_fn[key]) return this;
4758 val = global_objs[e.name];
4759 } else {
4760 val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
4761 if (val == null || val === e) return this;
4762 var native_fn = native_fns[val.constructor.name];
4763 if (!native_fn || !native_fn[key]) return this;
4764 if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
4765 }
4766 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4767 if (!args) return this;
4768 if (key == "replace" && typeof args[1] == "function") return this;
4769 try {
4770 return val[key].apply(val, args);
4771 } catch (ex) {
4772 AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
4773 code: this,
4774 file: this.start.file,
4775 line: this.start.line,
4776 col: this.start.col,
4777 });
4778 } finally {
4779 if (val instanceof RegExp) val.lastIndex = 0;
4780 }
4781 }
4782 return this;
4783
4784 function assign(sym, arg) {
4785 if (sym instanceof AST_DefaultValue) sym = sym.name;
4786 var def = sym.definition();
4787 if (def.orig[def.orig.length - 1] !== sym) return false;
4788 var value = arg;
4789 def.references.forEach(function(node) {
4790 node._eval = function() {
4791 return value;
4792 };
4793 cached_args.push(node);
4794 });
4795 return true;
4796 }
4797 });
4798 def(AST_New, return_this);
4799 def(AST_Template, function(compressor, ignore_side_effects, cached, depth) {
4800 if (!compressor.option("templates")) return this;
4801 if (this.tag) {
4802 if (!is_raw_tag(compressor, this.tag)) return this;
4803 decode = function(str) {
4804 return str;
4805 };
4806 }
4807 var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth);
4808 if (!exprs) return this;
4809 var malformed = false;
4810 var ret = decode(this.strings[0]);
4811 for (var i = 0; i < exprs.length; i++) {
4812 ret += exprs[i] + decode(this.strings[i + 1]);
4813 }
4814 if (!malformed) return ret;
4815 this._eval = return_this;
4816 return this;
4817
4818 function decode(str) {
4819 return str.replace(/\\(u\{[^}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
4820 var s = decode_escape_sequence(seq);
4821 if (typeof s != "string") malformed = true;
4822 return s;
4823 });
4824 }
4825 });
4826 })(function(node, func) {
4827 node.DEFMETHOD("_eval", func);
4828 });
4829
4830 // method to negate an expression
4831 (function(def) {
4832 function basic_negation(exp) {
4833 return make_node(AST_UnaryPrefix, exp, {
4834 operator: "!",
4835 expression: exp
4836 });
4837 }
4838 function best(orig, alt, first_in_statement) {
4839 var negated = basic_negation(orig);
4840 if (first_in_statement) {
4841 var stat = make_node(AST_SimpleStatement, alt, {
4842 body: alt
4843 });
4844 return best_of_expression(negated, stat) === stat ? alt : negated;
4845 }
4846 return best_of_expression(negated, alt);
4847 }
4848 def(AST_Node, function() {
4849 return basic_negation(this);
4850 });
4851 def(AST_Statement, function() {
4852 throw new Error("Cannot negate a statement");
4853 });
4854 def(AST_Binary, function(compressor, first_in_statement) {
4855 var self = this.clone(), op = this.operator;
4856 if (compressor.option("unsafe_comps")) {
4857 switch (op) {
4858 case "<=" : self.operator = ">" ; return self;
4859 case "<" : self.operator = ">=" ; return self;
4860 case ">=" : self.operator = "<" ; return self;
4861 case ">" : self.operator = "<=" ; return self;
4862 }
4863 }
4864 switch (op) {
4865 case "==" : self.operator = "!="; return self;
4866 case "!=" : self.operator = "=="; return self;
4867 case "===": self.operator = "!=="; return self;
4868 case "!==": self.operator = "==="; return self;
4869 case "&&":
4870 self.operator = "||";
4871 self.left = self.left.negate(compressor, first_in_statement);
4872 self.right = self.right.negate(compressor);
4873 return best(this, self, first_in_statement);
4874 case "||":
4875 self.operator = "&&";
4876 self.left = self.left.negate(compressor, first_in_statement);
4877 self.right = self.right.negate(compressor);
4878 return best(this, self, first_in_statement);
4879 }
4880 return basic_negation(this);
4881 });
4882 def(AST_ClassExpression, function() {
4883 return basic_negation(this);
4884 });
4885 def(AST_Conditional, function(compressor, first_in_statement) {
4886 var self = this.clone();
4887 self.consequent = self.consequent.negate(compressor);
4888 self.alternative = self.alternative.negate(compressor);
4889 return best(this, self, first_in_statement);
4890 });
4891 def(AST_LambdaExpression, function() {
4892 return basic_negation(this);
4893 });
4894 def(AST_Sequence, function(compressor) {
4895 var expressions = this.expressions.slice();
4896 expressions.push(expressions.pop().negate(compressor));
4897 return make_sequence(this, expressions);
4898 });
4899 def(AST_UnaryPrefix, function() {
4900 if (this.operator == "!")
4901 return this.expression;
4902 return basic_negation(this);
4903 });
4904 })(function(node, func) {
4905 node.DEFMETHOD("negate", function(compressor, first_in_statement) {
4906 return func.call(this, compressor, first_in_statement);
4907 });
4908 });
4909
4910 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");
4911 var global_pure_constructors = makePredicate("Map Set WeakMap WeakSet");
4912 AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
4913 if (compressor.option("unsafe")) {
4914 var expr = this.expression;
4915 if (is_undeclared_ref(expr)) {
4916 if (global_pure_fns[expr.name]) return true;
4917 if (this instanceof AST_New && global_pure_constructors[expr.name]) return true;
4918 }
4919 if (expr instanceof AST_Dot && is_undeclared_ref(expr.expression)) {
4920 var static_fn = static_fns[expr.expression.name];
4921 return static_fn && (static_fn[expr.property]
4922 || expr.expression.name == "Math" && expr.property == "random");
4923 }
4924 }
4925 return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this);
4926 });
4927 AST_Template.DEFMETHOD("is_expr_pure", function(compressor) {
4928 var tag = this.tag;
4929 if (!tag) return true;
4930 if (compressor.option("unsafe")) {
4931 if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true;
4932 if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) {
4933 var static_fn = static_fns[tag.expression.name];
4934 return static_fn && (static_fn[tag.property]
4935 || tag.expression.name == "Math" && tag.property == "random");
4936 }
4937 }
4938 return !compressor.pure_funcs(this);
4939 });
4940 AST_Node.DEFMETHOD("is_call_pure", return_false);
4941 AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
4942 if (!compressor.option("unsafe")) return false;
4943 var dot = this.expression;
4944 if (!(dot instanceof AST_Dot)) return false;
4945 var exp = dot.expression;
4946 var map;
4947 var prop = dot.property;
4948 if (exp instanceof AST_Array) {
4949 map = native_fns.Array;
4950 } else if (exp.is_boolean(compressor)) {
4951 map = native_fns.Boolean;
4952 } else if (exp.is_number(compressor)) {
4953 map = native_fns.Number;
4954 } else if (exp instanceof AST_RegExp) {
4955 map = native_fns.RegExp;
4956 } else if (exp.is_string(compressor)) {
4957 map = native_fns.String;
4958 if (prop == "replace") {
4959 var arg = this.args[1];
4960 if (arg && !arg.is_string(compressor)) return false;
4961 }
4962 } else if (!dot.may_throw_on_access(compressor)) {
4963 map = native_fns.Object;
4964 }
4965 return map && map[prop];
4966 });
4967
4968 function spread_side_effects(exp) {
4969 while ((exp = exp.tail_node()) instanceof AST_SymbolRef) {
4970 exp = exp.fixed_value();
4971 if (!exp) return true;
4972 }
4973 return !(exp instanceof AST_Array
4974 || exp.TYPE == "Binary" && !lazy_op[exp.operator]
4975 || exp instanceof AST_Constant
4976 || exp instanceof AST_Lambda
4977 || exp instanceof AST_Object && all(exp.properties, function(prop) {
4978 return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
4979 })
4980 || exp instanceof AST_ObjectIdentity
4981 || exp instanceof AST_Unary);
4982 }
4983
4984 // determine if expression has side effects
4985 (function(def) {
4986 function any(list, compressor, spread) {
4987 return !all(list, spread ? function(node) {
4988 return node instanceof AST_Spread ? !spread(node, compressor) : !node.has_side_effects(compressor);
4989 } : function(node) {
4990 return !node.has_side_effects(compressor);
4991 });
4992 }
4993 function array_spread(node, compressor) {
4994 return !node.expression.is_string(compressor) || node.expression.has_side_effects(compressor);
4995 }
4996 def(AST_Node, return_true);
4997 def(AST_Array, function(compressor) {
4998 return any(this.elements, compressor, array_spread);
4999 });
5000 def(AST_Assign, function(compressor) {
5001 var lhs = this.left;
5002 if (!(lhs instanceof AST_PropAccess)) return true;
5003 var node = lhs.expression;
5004 return !(node instanceof AST_ObjectIdentity)
5005 || !node.scope.new
5006 || lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
5007 || this.right.has_side_effects(compressor);
5008 });
5009 def(AST_Binary, function(compressor) {
5010 return this.left.has_side_effects(compressor)
5011 || this.right.has_side_effects(compressor)
5012 || this.operator == "in" && !is_object(this.right);
5013 });
5014 def(AST_Block, function(compressor) {
5015 return any(this.body, compressor);
5016 });
5017 def(AST_Call, function(compressor) {
5018 if (!this.is_expr_pure(compressor)
5019 && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) {
5020 return true;
5021 }
5022 return any(this.args, compressor, array_spread);
5023 });
5024 def(AST_Case, function(compressor) {
5025 return this.expression.has_side_effects(compressor)
5026 || any(this.body, compressor);
5027 });
5028 def(AST_Class, function(compressor) {
5029 var base = this.extends;
5030 if (base) {
5031 if (base instanceof AST_SymbolRef) base = base.fixed_value();
5032 if (!safe_for_extends(base)) return true;
5033 }
5034 return any(this.properties, compressor);
5035 });
5036 def(AST_ClassProperty, function(compressor) {
5037 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5038 || this.static && this.value && this.value.has_side_effects(compressor);
5039 });
5040 def(AST_Conditional, function(compressor) {
5041 return this.condition.has_side_effects(compressor)
5042 || this.consequent.has_side_effects(compressor)
5043 || this.alternative.has_side_effects(compressor);
5044 });
5045 def(AST_Constant, return_false);
5046 def(AST_Definitions, function(compressor) {
5047 return any(this.definitions, compressor);
5048 });
5049 def(AST_DestructuredArray, function(compressor) {
5050 return any(this.elements, compressor);
5051 });
5052 def(AST_DestructuredKeyVal, function(compressor) {
5053 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5054 || this.value.has_side_effects(compressor);
5055 });
5056 def(AST_DestructuredObject, function(compressor) {
5057 return any(this.properties, compressor);
5058 });
5059 def(AST_Dot, function(compressor) {
5060 return !this.optional && this.expression.may_throw_on_access(compressor)
5061 || this.expression.has_side_effects(compressor);
5062 });
5063 def(AST_EmptyStatement, return_false);
5064 def(AST_If, function(compressor) {
5065 return this.condition.has_side_effects(compressor)
5066 || this.body && this.body.has_side_effects(compressor)
5067 || this.alternative && this.alternative.has_side_effects(compressor);
5068 });
5069 def(AST_LabeledStatement, function(compressor) {
5070 return this.body.has_side_effects(compressor);
5071 });
5072 def(AST_Lambda, return_false);
5073 def(AST_Object, function(compressor) {
5074 return any(this.properties, compressor, function(node, compressor) {
5075 var exp = node.expression;
5076 return spread_side_effects(exp) || exp.has_side_effects(compressor);
5077 });
5078 });
5079 def(AST_ObjectIdentity, return_false);
5080 def(AST_ObjectProperty, function(compressor) {
5081 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5082 || this.value.has_side_effects(compressor);
5083 });
5084 def(AST_Sequence, function(compressor) {
5085 return any(this.expressions, compressor);
5086 });
5087 def(AST_SimpleStatement, function(compressor) {
5088 return this.body.has_side_effects(compressor);
5089 });
5090 def(AST_Sub, function(compressor) {
5091 return !this.optional && this.expression.may_throw_on_access(compressor)
5092 || this.expression.has_side_effects(compressor)
5093 || this.property.has_side_effects(compressor);
5094 });
5095 def(AST_Switch, function(compressor) {
5096 return this.expression.has_side_effects(compressor)
5097 || any(this.body, compressor);
5098 });
5099 def(AST_SymbolDeclaration, return_false);
5100 def(AST_SymbolRef, function(compressor) {
5101 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5102 });
5103 def(AST_Template, function(compressor) {
5104 return !this.is_expr_pure(compressor) || any(this.expressions, compressor);
5105 });
5106 def(AST_Try, function(compressor) {
5107 return any(this.body, compressor)
5108 || this.bcatch && this.bcatch.has_side_effects(compressor)
5109 || this.bfinally && this.bfinally.has_side_effects(compressor);
5110 });
5111 def(AST_Unary, function(compressor) {
5112 return unary_side_effects[this.operator]
5113 || this.expression.has_side_effects(compressor);
5114 });
5115 def(AST_VarDef, function() {
5116 return this.value;
5117 });
5118 })(function(node, func) {
5119 node.DEFMETHOD("has_side_effects", func);
5120 });
5121
5122 // determine if expression may throw
5123 (function(def) {
5124 def(AST_Node, return_true);
5125
5126 def(AST_Constant, return_false);
5127 def(AST_EmptyStatement, return_false);
5128 def(AST_Lambda, return_false);
5129 def(AST_ObjectIdentity, return_false);
5130 def(AST_SymbolDeclaration, return_false);
5131
5132 function any(list, compressor) {
5133 for (var i = list.length; --i >= 0;)
5134 if (list[i].may_throw(compressor))
5135 return true;
5136 return false;
5137 }
5138
5139 def(AST_Array, function(compressor) {
5140 return any(this.elements, compressor);
5141 });
5142 def(AST_Assign, function(compressor) {
5143 if (this.right.may_throw(compressor)) return true;
5144 if (!compressor.has_directive("use strict")
5145 && this.operator == "="
5146 && this.left instanceof AST_SymbolRef) {
5147 return false;
5148 }
5149 return this.left.may_throw(compressor);
5150 });
5151 def(AST_Binary, function(compressor) {
5152 return this.left.may_throw(compressor)
5153 || this.right.may_throw(compressor)
5154 || this.operator == "in" && !is_object(this.right);
5155 });
5156 def(AST_Block, function(compressor) {
5157 return any(this.body, compressor);
5158 });
5159 def(AST_Call, function(compressor) {
5160 if (any(this.args, compressor)) return true;
5161 if (this.is_expr_pure(compressor)) return false;
5162 if (this.expression.may_throw(compressor)) return true;
5163 return !(this.expression instanceof AST_Lambda)
5164 || any(this.expression.body, compressor);
5165 });
5166 def(AST_Case, function(compressor) {
5167 return this.expression.may_throw(compressor)
5168 || any(this.body, compressor);
5169 });
5170 def(AST_Conditional, function(compressor) {
5171 return this.condition.may_throw(compressor)
5172 || this.consequent.may_throw(compressor)
5173 || this.alternative.may_throw(compressor);
5174 });
5175 def(AST_Definitions, function(compressor) {
5176 return any(this.definitions, compressor);
5177 });
5178 def(AST_Dot, function(compressor) {
5179 return !this.optional && this.expression.may_throw_on_access(compressor)
5180 || this.expression.may_throw(compressor);
5181 });
5182 def(AST_If, function(compressor) {
5183 return this.condition.may_throw(compressor)
5184 || this.body && this.body.may_throw(compressor)
5185 || this.alternative && this.alternative.may_throw(compressor);
5186 });
5187 def(AST_LabeledStatement, function(compressor) {
5188 return this.body.may_throw(compressor);
5189 });
5190 def(AST_Object, function(compressor) {
5191 return any(this.properties, compressor);
5192 });
5193 def(AST_ObjectProperty, function(compressor) {
5194 return this.key instanceof AST_Node && this.key.may_throw(compressor)
5195 || this.value.may_throw(compressor);
5196 });
5197 def(AST_Return, function(compressor) {
5198 return this.value && this.value.may_throw(compressor);
5199 });
5200 def(AST_Sequence, function(compressor) {
5201 return any(this.expressions, compressor);
5202 });
5203 def(AST_SimpleStatement, function(compressor) {
5204 return this.body.may_throw(compressor);
5205 });
5206 def(AST_Sub, function(compressor) {
5207 return !this.optional && this.expression.may_throw_on_access(compressor)
5208 || this.expression.may_throw(compressor)
5209 || this.property.may_throw(compressor);
5210 });
5211 def(AST_Switch, function(compressor) {
5212 return this.expression.may_throw(compressor)
5213 || any(this.body, compressor);
5214 });
5215 def(AST_SymbolRef, function(compressor) {
5216 return !this.is_declared(compressor);
5217 });
5218 def(AST_Try, function(compressor) {
5219 return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor))
5220 || this.bfinally && this.bfinally.may_throw(compressor);
5221 });
5222 def(AST_Unary, function(compressor) {
5223 if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef)
5224 return false;
5225 return this.expression.may_throw(compressor);
5226 });
5227 def(AST_VarDef, function(compressor) {
5228 if (!this.value) return false;
5229 return this.value.may_throw(compressor);
5230 });
5231 })(function(node, func) {
5232 node.DEFMETHOD("may_throw", func);
5233 });
5234
5235 // determine if expression is constant
5236 (function(def) {
5237 function all_constant(list, scope) {
5238 for (var i = list.length; --i >= 0;)
5239 if (!list[i].is_constant_expression(scope))
5240 return false;
5241 return true;
5242 }
5243 def(AST_Node, return_false);
5244 def(AST_Array, function(scope) {
5245 return all_constant(this.elements, scope);
5246 });
5247 def(AST_Binary, function(scope) {
5248 return this.left.is_constant_expression(scope)
5249 && this.right.is_constant_expression(scope)
5250 && (this.operator != "in" || is_object(this.right));
5251 });
5252 def(AST_Class, function(scope) {
5253 var base = this.extends;
5254 if (base && !safe_for_extends(base)) return false;
5255 return all_constant(this.properties, scope);
5256 });
5257 def(AST_ClassProperty, function(scope) {
5258 return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope));
5259 });
5260 def(AST_Constant, return_true);
5261 def(AST_Lambda, function(scope) {
5262 var self = this;
5263 var result = true;
5264 var scopes = [];
5265 self.walk(new TreeWalker(function(node, descend) {
5266 if (!result) return true;
5267 if (node instanceof AST_BlockScope) {
5268 if (node === self) return;
5269 scopes.push(node);
5270 descend();
5271 scopes.pop();
5272 return true;
5273 }
5274 if (node instanceof AST_SymbolRef) {
5275 if (self.inlined || node.redef) {
5276 result = false;
5277 return true;
5278 }
5279 if (self.variables.has(node.name)) return true;
5280 var def = node.definition();
5281 if (member(def.scope, scopes)) return true;
5282 if (scope && !def.redefined()) {
5283 var scope_def = scope.find_variable(node.name);
5284 if (scope_def ? scope_def === def : def.undeclared) {
5285 result = "f";
5286 return true;
5287 }
5288 }
5289 result = false;
5290 return true;
5291 }
5292 if (node instanceof AST_ObjectIdentity) {
5293 if (is_arrow(self) && all(scopes, function(s) {
5294 return !(s instanceof AST_Scope) || is_arrow(s);
5295 })) result = false;
5296 return true;
5297 }
5298 }));
5299 return result;
5300 });
5301 def(AST_Object, function(scope) {
5302 return all_constant(this.properties, scope);
5303 });
5304 def(AST_ObjectProperty, function(scope) {
5305 return typeof this.key == "string" && this.value.is_constant_expression(scope);
5306 });
5307 def(AST_Unary, function(scope) {
5308 return this.expression.is_constant_expression(scope);
5309 });
5310 })(function(node, func) {
5311 node.DEFMETHOD("is_constant_expression", func);
5312 });
5313
5314 // tell me if a statement aborts
5315 function aborts(thing) {
5316 return thing && thing.aborts();
5317 }
5318 (function(def) {
5319 def(AST_Statement, return_null);
5320 def(AST_Jump, return_this);
5321 function block_aborts() {
5322 var n = this.body.length;
5323 return n > 0 && aborts(this.body[n - 1]);
5324 }
5325 def(AST_BlockStatement, block_aborts);
5326 def(AST_SwitchBranch, block_aborts);
5327 def(AST_If, function() {
5328 return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
5329 });
5330 })(function(node, func) {
5331 node.DEFMETHOD("aborts", func);
5332 });
5333
5334 /* -----[ optimizers ]----- */
5335
5336 var directives = makePredicate(["use asm", "use strict"]);
5337 OPT(AST_Directive, function(self, compressor) {
5338 if (compressor.option("directives")
5339 && (!directives[self.value] || compressor.has_directive(self.value) !== self)) {
5340 return make_node(AST_EmptyStatement, self);
5341 }
5342 return self;
5343 });
5344
5345 OPT(AST_Debugger, function(self, compressor) {
5346 if (compressor.option("drop_debugger"))
5347 return make_node(AST_EmptyStatement, self);
5348 return self;
5349 });
5350
5351 OPT(AST_LabeledStatement, function(self, compressor) {
5352 if (compressor.option("dead_code")
5353 && self.body instanceof AST_Break
5354 && compressor.loopcontrol_target(self.body) === self.body) {
5355 return make_node(AST_EmptyStatement, self);
5356 }
5357 return compressor.option("unused") && self.label.references.length == 0 ? self.body : self;
5358 });
5359
5360 OPT(AST_Block, function(self, compressor) {
5361 self.body = tighten_body(self.body, compressor);
5362 return self;
5363 });
5364
5365 function trim_block(node, parent, in_list) {
5366 switch (node.body.length) {
5367 case 0:
5368 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
5369 case 1:
5370 var stat = node.body[0];
5371 if (!safe_to_trim(stat)) return node;
5372 if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node;
5373 return stat;
5374 }
5375 return node;
5376 }
5377
5378 OPT(AST_BlockStatement, function(self, compressor) {
5379 self.body = tighten_body(self.body, compressor);
5380 return trim_block(self, compressor.parent());
5381 });
5382
5383 function drop_rest_farg(fn, compressor) {
5384 if (!compressor.option("rests")) return;
5385 if (fn.uses_arguments) return;
5386 if (!(fn.rest instanceof AST_DestructuredArray)) return;
5387 if (!compressor.drop_fargs(fn, compressor.parent())) return;
5388 fn.argnames = fn.argnames.concat(fn.rest.elements);
5389 fn.rest = null;
5390 }
5391
5392 OPT(AST_Lambda, function(self, compressor) {
5393 drop_rest_farg(self, compressor);
5394 self.body = tighten_body(self.body, compressor);
5395 return self;
5396 });
5397
5398 function opt_arrow(self, compressor) {
5399 if (!compressor.option("arrows")) return self;
5400 drop_rest_farg(self, compressor);
5401 var body = tighten_body(self.value ? [ self.first_statement() ] : self.body, compressor);
5402 switch (body.length) {
5403 case 1:
5404 var stat = body[0];
5405 if (stat instanceof AST_Return) {
5406 self.body.length = 0;
5407 self.value = stat.value;
5408 break;
5409 }
5410 default:
5411 self.body = body;
5412 self.value = null;
5413 break;
5414 }
5415 return self;
5416 }
5417 OPT(AST_Arrow, opt_arrow);
5418 OPT(AST_AsyncArrow, opt_arrow);
5419
5420 OPT(AST_Function, function(self, compressor) {
5421 drop_rest_farg(self, compressor);
5422 self.body = tighten_body(self.body, compressor);
5423 var parent = compressor.parent();
5424 if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
5425 var stat = self.body[i];
5426 if (stat instanceof AST_Directive) continue;
5427 if (stat instanceof AST_Return) {
5428 if (i != self.body.length - 1) break;
5429 var call = stat.value;
5430 if (!call || call.TYPE != "Call") break;
5431 if (call.is_expr_pure(compressor)) break;
5432 var fn = call.expression;
5433 if (fn instanceof AST_SymbolRef) {
5434 if (self.name && self.name.definition() === fn.definition()) break;
5435 fn = fn.fixed_value();
5436 }
5437 if (!(fn instanceof AST_Defun || fn instanceof AST_Function)) break;
5438 if (fn.uses_arguments) break;
5439 if (fn === call.expression) {
5440 if (fn.parent_scope !== self) break;
5441 if (!all(fn.enclosed, function(def) {
5442 return def.scope !== self;
5443 })) break;
5444 }
5445 if (fn.name
5446 && (parent instanceof AST_ClassMethod || parent instanceof AST_ObjectMethod)
5447 && parent.value === compressor.self()) break;
5448 if (fn.contains_this()) break;
5449 var len = fn.argnames.length;
5450 if (len > 0 && compressor.option("inline") < 2) break;
5451 if (len > self.argnames.length) break;
5452 if (!all(self.argnames, function(argname) {
5453 return argname instanceof AST_SymbolFunarg;
5454 })) break;
5455 if (!all(call.args, function(arg) {
5456 return !(arg instanceof AST_Spread);
5457 })) break;
5458 for (var j = 0; j < len; j++) {
5459 var arg = call.args[j];
5460 if (!(arg instanceof AST_SymbolRef)) break;
5461 if (arg.definition() !== self.argnames[j].definition()) break;
5462 }
5463 if (j < len) break;
5464 for (; j < call.args.length; j++) {
5465 if (call.args[j].has_side_effects(compressor)) break;
5466 }
5467 if (j < call.args.length) break;
5468 if (len < self.argnames.length && !compressor.drop_fargs(self, parent)) {
5469 if (!compressor.drop_fargs(fn, call)) break;
5470 do {
5471 fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len));
5472 } while (++len < self.argnames.length);
5473 }
5474 return call.expression;
5475 }
5476 break;
5477 }
5478 return self;
5479 });
5480
5481 var NO_MERGE = makePredicate("arguments await yield");
5482 AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
5483 if (!compressor.option("merge_vars")) return;
5484 var in_try, root, segment = {}, self = this;
5485 var first = [], last = [], index = 0;
5486 var declarations = new Dictionary();
5487 var references = Object.create(null);
5488 var prev = Object.create(null);
5489 var tw = new TreeWalker(function(node, descend) {
5490 if (node instanceof AST_Assign) {
5491 var lhs = node.left;
5492 var rhs = node.right;
5493 if (lhs instanceof AST_Destructured) {
5494 rhs.walk(tw);
5495 var marker = new TreeWalker(function(node) {
5496 if (node instanceof AST_Destructured) return;
5497 if (node instanceof AST_DefaultValue) {
5498 push();
5499 node.value.walk(tw);
5500 pop();
5501 node.name.walk(marker);
5502 } else if (node instanceof AST_DestructuredKeyVal) {
5503 if (node.key instanceof AST_Node) {
5504 push();
5505 segment.block = node;
5506 node.key.walk(tw);
5507 node.value.walk(marker);
5508 pop();
5509 } else {
5510 node.value.walk(marker);
5511 }
5512 } else if (node instanceof AST_SymbolRef) {
5513 mark(node);
5514 } else {
5515 node.walk(tw);
5516 }
5517 return true;
5518 });
5519 lhs.walk(marker);
5520 return true;
5521 }
5522 if (lazy_op[node.operator.slice(0, -1)]) {
5523 lhs.walk(tw);
5524 push();
5525 rhs.walk(tw);
5526 if (lhs instanceof AST_SymbolRef) mark(lhs);
5527 pop();
5528 return true;
5529 }
5530 if (lhs instanceof AST_SymbolRef) {
5531 if (node.operator != "=") mark(lhs, true);
5532 rhs.walk(tw);
5533 mark(lhs);
5534 return true;
5535 }
5536 return;
5537 }
5538 if (node instanceof AST_Binary) {
5539 if (!lazy_op[node.operator]) return;
5540 node.left.walk(tw);
5541 push();
5542 node.right.walk(tw);
5543 pop();
5544 return true;
5545 }
5546 if (node instanceof AST_Break) {
5547 var target = tw.loopcontrol_target(node);
5548 if (!(target instanceof AST_IterationStatement)) insert(target);
5549 return true;
5550 }
5551 if (node instanceof AST_Call) {
5552 var exp = node.expression;
5553 var tail = exp.tail_node();
5554 if (!(tail instanceof AST_LambdaExpression)) return;
5555 if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
5556 node.walk(tw);
5557 });
5558 node.args.forEach(function(arg) {
5559 arg.walk(tw);
5560 });
5561 tail.walk(tw);
5562 return true;
5563 }
5564 if (node instanceof AST_Conditional) {
5565 node.condition.walk(tw);
5566 push();
5567 node.consequent.walk(tw);
5568 pop();
5569 push();
5570 node.alternative.walk(tw);
5571 pop();
5572 return true;
5573 }
5574 if (node instanceof AST_Continue) {
5575 var target = tw.loopcontrol_target(node);
5576 if (target instanceof AST_Do) insert(target);
5577 return true;
5578 }
5579 if (node instanceof AST_Do) {
5580 push();
5581 segment.block = node;
5582 segment.loop = true;
5583 var save = segment;
5584 node.body.walk(tw);
5585 if (segment.inserted === node) segment = save;
5586 node.condition.walk(tw);
5587 pop();
5588 return true;
5589 }
5590 if (node instanceof AST_For) {
5591 if (node.init) node.init.walk(tw);
5592 push();
5593 segment.block = node;
5594 segment.loop = true;
5595 if (node.condition) node.condition.walk(tw);
5596 node.body.walk(tw);
5597 if (node.step) node.step.walk(tw);
5598 pop();
5599 return true;
5600 }
5601 if (node instanceof AST_ForEnumeration) {
5602 node.object.walk(tw);
5603 push();
5604 segment.block = node;
5605 segment.loop = true;
5606 node.init.walk(tw);
5607 node.body.walk(tw);
5608 pop();
5609 return true;
5610 }
5611 if (node instanceof AST_If) {
5612 node.condition.walk(tw);
5613 push();
5614 node.body.walk(tw);
5615 pop();
5616 if (node.alternative) {
5617 push();
5618 node.alternative.walk(tw);
5619 pop();
5620 }
5621 return true;
5622 }
5623 if (node instanceof AST_LabeledStatement) {
5624 push();
5625 segment.block = node;
5626 var save = segment;
5627 node.body.walk(tw);
5628 if (segment.inserted === node) segment = save;
5629 pop();
5630 return true;
5631 }
5632 if (node instanceof AST_Scope) {
5633 push();
5634 segment.block = node;
5635 if (node === self) root = segment;
5636 if (node instanceof AST_Lambda) {
5637 if (node.name) references[node.name.definition().id] = false;
5638 var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) {
5639 if (node instanceof AST_SymbolFunarg) references[node.definition().id] = false;
5640 } : function(node) {
5641 if (node instanceof AST_SymbolFunarg) mark(node);
5642 };
5643 var scanner = new TreeWalker(function(ref) {
5644 if (ref instanceof AST_SymbolDeclaration) references[ref.definition().id] = false;
5645 if (!(ref instanceof AST_SymbolRef)) return;
5646 var def = ref.definition();
5647 var ldef = node.variables.get(ref.name);
5648 if (ldef && (ldef === def
5649 || def.undeclared
5650 || node.parent_scope.find_variable(ref.name) === def)) {
5651 references[def.id] = false;
5652 references[ldef.id] = false;
5653 } else {
5654 var save = segment;
5655 pop();
5656 mark(ref, true);
5657 segment = save;
5658 }
5659 return true;
5660 });
5661 node.argnames.forEach(function(argname) {
5662 argname.mark_symbol(marker, scanner);
5663 });
5664 if (node.rest) node.rest.mark_symbol(marker, scanner);
5665 }
5666 walk_lambda(node, tw);
5667 pop();
5668 return true;
5669 }
5670 if (node instanceof AST_Switch) {
5671 node.expression.walk(tw);
5672 var save = segment;
5673 node.body.forEach(function(branch) {
5674 if (branch instanceof AST_Default) return;
5675 branch.expression.walk(tw);
5676 if (save === segment) push();
5677 });
5678 segment = save;
5679 node.body.forEach(function(branch) {
5680 push();
5681 segment.block = node;
5682 var save = segment;
5683 walk_body(branch, tw);
5684 if (segment.inserted === node) segment = save;
5685 pop();
5686 });
5687 return true;
5688 }
5689 if (node instanceof AST_SymbolConst || node instanceof AST_SymbolLet) {
5690 references[node.definition().id] = false;
5691 return true;
5692 }
5693 if (node instanceof AST_SymbolRef) {
5694 mark(node, true);
5695 return true;
5696 }
5697 if (node instanceof AST_Try) {
5698 var save_try = in_try;
5699 in_try = node;
5700 var save = segment;
5701 walk_body(node, tw);
5702 segment = save;
5703 if (node.bcatch) {
5704 if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
5705 if (node instanceof AST_SymbolCatch) {
5706 var def = node.definition();
5707 references[def.id] = false;
5708 if (def = def.redefined()) references[def.id] = false;
5709 }
5710 }, tw);
5711 if (node.bfinally || (in_try = save_try)) {
5712 walk_body(node.bcatch, tw);
5713 } else {
5714 push();
5715 walk_body(node.bcatch, tw);
5716 pop();
5717 }
5718 }
5719 in_try = save_try;
5720 segment = save;
5721 if (node.bfinally) node.bfinally.walk(tw);
5722 return true;
5723 }
5724 if (node instanceof AST_Unary) {
5725 if (!UNARY_POSTFIX[node.operator]) return;
5726 var sym = node.expression;
5727 if (!(sym instanceof AST_SymbolRef)) return;
5728 mark(sym, true);
5729 return true;
5730 }
5731 if (node instanceof AST_VarDef) {
5732 var assigned = node.value;
5733 if (assigned) {
5734 assigned.walk(tw);
5735 } else {
5736 assigned = segment.block instanceof AST_ForEnumeration && segment.block.init === tw.parent();
5737 }
5738 node.name.mark_symbol(assigned ? function(node) {
5739 if (!(node instanceof AST_SymbolDeclaration)) return;
5740 if (node instanceof AST_SymbolVar) {
5741 mark(node);
5742 } else {
5743 references[node.definition().id] = false;
5744 }
5745 return true;
5746 } : function(node) {
5747 if (!(node instanceof AST_SymbolDeclaration)) return;
5748 var id = node.definition().id;
5749 if (!(node instanceof AST_SymbolVar)) {
5750 references[id] = false;
5751 } else if (!(id in references)) {
5752 declarations.add(id, node);
5753 } else if (references[id]) {
5754 references[id].push(node);
5755 }
5756 return true;
5757 }, tw);
5758 return true;
5759 }
5760 if (node instanceof AST_While) {
5761 push();
5762 segment.block = node;
5763 segment.loop = true;
5764 descend();
5765 pop();
5766 return true;
5767 }
5768 });
5769 tw.directives = Object.create(compressor.directives);
5770 self.walk(tw);
5771 var merged = Object.create(null);
5772 while (first.length && last.length) {
5773 var head = first.pop();
5774 var def = head.definition;
5775 if (!(def.id in prev)) continue;
5776 if (!references[def.id]) continue;
5777 var head_refs = {
5778 start: references[def.id].start,
5779 };
5780 while (def.id in merged) def = merged[def.id];
5781 head_refs.end = references[def.id].end;
5782 var skipped = [];
5783 do {
5784 var tail = last.pop();
5785 if (!tail) continue;
5786 if (tail.index > head.index) continue;
5787 var id = tail.definition.id;
5788 var tail_refs = references[id];
5789 if (!tail_refs) continue;
5790 if (head_refs.start.block !== tail_refs.start.block
5791 || !mergeable(head_refs, tail_refs)
5792 || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs)
5793 || !all(tail_refs, function(sym) {
5794 return sym.scope.find_variable(def.name) === def;
5795 })) {
5796 skipped.unshift(tail);
5797 continue;
5798 }
5799 var orig = [], refs = [];
5800 tail_refs.forEach(function(sym) {
5801 sym.thedef = def;
5802 sym.name = def.name;
5803 if (sym instanceof AST_SymbolRef) {
5804 refs.push(sym);
5805 } else {
5806 orig.push(sym);
5807 }
5808 });
5809 def.orig = orig.concat(def.orig);
5810 def.references = refs.concat(def.references);
5811 def.fixed = tail.definition.fixed && def.fixed;
5812 merged[id] = def;
5813 break;
5814 } while (last.length);
5815 if (skipped.length) last = last.concat(skipped);
5816 }
5817
5818 function push() {
5819 segment = Object.create(segment);
5820 }
5821
5822 function pop() {
5823 segment = Object.getPrototypeOf(segment);
5824 }
5825
5826 function mark(sym, read) {
5827 var def = sym.definition(), ldef, seg = segment;
5828 if (in_try) {
5829 push();
5830 seg = segment;
5831 pop();
5832 }
5833 if (def.id in references) {
5834 var refs = references[def.id];
5835 if (!refs) return;
5836 if (refs.start.block !== seg.block) return references[def.id] = false;
5837 refs.push(sym);
5838 refs.end = seg;
5839 if (def.id in prev) {
5840 last[prev[def.id]] = null;
5841 } else if (!read) {
5842 return;
5843 }
5844 } else if ((ldef = self.variables.get(def.name)) !== def) {
5845 if (ldef && root === seg) references[ldef.id] = false;
5846 return references[def.id] = false;
5847 } else if (compressor.exposed(def) || NO_MERGE[sym.name]) {
5848 return references[def.id] = false;
5849 } else {
5850 var refs = declarations.get(def.id) || [];
5851 refs.push(sym);
5852 references[def.id] = refs;
5853 if (!read) {
5854 refs.start = seg;
5855 return first.push({
5856 index: index++,
5857 definition: def,
5858 });
5859 }
5860 if (seg.block !== self) return references[def.id] = false;
5861 refs.start = root;
5862 }
5863 prev[def.id] = last.length;
5864 last.push({
5865 index: index++,
5866 definition: def,
5867 });
5868 }
5869
5870 function insert(target) {
5871 var stack = [];
5872 while (true) {
5873 if (HOP(segment, "block")) {
5874 var block = segment.block;
5875 if (block instanceof AST_LabeledStatement) block = block.body;
5876 if (block === target) break;
5877 }
5878 stack.push(segment);
5879 pop();
5880 }
5881 segment.inserted = segment.block;
5882 push();
5883 while (stack.length) {
5884 var seg = stack.pop();
5885 push();
5886 if (HOP(seg, "block")) segment.block = seg.block;
5887 if (HOP(seg, "loop")) segment.loop = seg.loop;
5888 }
5889 }
5890
5891 function must_visit(base, segment) {
5892 return base === segment || base.isPrototypeOf(segment);
5893 }
5894
5895 function mergeable(head, tail) {
5896 return must_visit(head.start, head.end) || must_visit(head.start, tail.start);
5897 }
5898 });
5899
5900 function fill_holes(orig, elements) {
5901 for (var i = elements.length; --i >= 0;) {
5902 if (!elements[i]) elements[i] = make_node(AST_Hole, orig);
5903 }
5904 }
5905
5906 function to_class_expr(defcl, drop_name) {
5907 var cl = make_node(AST_ClassExpression, defcl, defcl);
5908 cl.name = drop_name ? null : make_node(AST_SymbolClass, defcl.name, defcl.name);
5909 return cl;
5910 }
5911
5912 function to_func_expr(defun, drop_name) {
5913 var ctor;
5914 switch (defun.CTOR) {
5915 case AST_AsyncDefun:
5916 ctor = AST_AsyncFunction;
5917 break;
5918 case AST_AsyncGeneratorDefun:
5919 ctor = AST_AsyncGeneratorFunction;
5920 break;
5921 case AST_Defun:
5922 ctor = AST_Function;
5923 break;
5924 case AST_GeneratorDefun:
5925 ctor = AST_GeneratorFunction;
5926 break;
5927 }
5928 var fn = make_node(ctor, defun, defun);
5929 fn.name = drop_name ? null : make_node(AST_SymbolLambda, defun.name, defun.name);
5930 return fn;
5931 }
5932
5933 AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
5934 if (!compressor.option("unused")) return;
5935 var self = this;
5936 var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
5937 var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
5938 var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
5939 var sym, nested = false;
5940 if (node instanceof AST_Assign) {
5941 if (node.write_only || node.operator == "=") sym = extract_reference(node.left, props);
5942 } else if (node instanceof AST_Unary) {
5943 if (node.write_only) sym = extract_reference(node.expression, props);
5944 }
5945 if (!(sym instanceof AST_SymbolRef)) return;
5946 var def = sym.definition();
5947 if (export_defaults[def.id]) return;
5948 if (compressor.exposed(def)) return;
5949 if (!can_drop_symbol(sym, compressor, nested)) return;
5950 return sym;
5951
5952 function extract_reference(node, props) {
5953 if (node instanceof AST_PropAccess) {
5954 var expr = node.expression;
5955 if (!expr.may_throw_on_access(compressor, true)) {
5956 nested = true;
5957 if (props && node instanceof AST_Sub) props.unshift(node.property);
5958 return extract_reference(expr, props);
5959 }
5960 } else if (node instanceof AST_Assign && node.operator == "=") {
5961 node.write_only = "p";
5962 var ref = extract_reference(node.right);
5963 if (!props) return ref;
5964 props.assign = node;
5965 return ref instanceof AST_SymbolRef ? ref : node.left;
5966 }
5967 return node;
5968 }
5969 };
5970 var assign_in_use = Object.create(null);
5971 var export_defaults = Object.create(null);
5972 var find_variable = function(name) {
5973 find_variable = compose(self, 0, noop);
5974 return find_variable(name);
5975
5976 function compose(child, level, find) {
5977 var parent = compressor.parent(level);
5978 if (!parent) return find;
5979 if (parent instanceof AST_DestructuredKeyVal) {
5980 var destructured = compressor.parent(level + 1);
5981 if (parent.key === child) {
5982 var fn = compressor.parent(level + 2);
5983 if (fn instanceof AST_Lambda) {
5984 return compose(fn, level + 3, fn.argnames.indexOf(destructured) >= 0 ? function(name) {
5985 var def = find(name);
5986 if (def) return def;
5987 def = fn.variables.get(name);
5988 if (def) {
5989 var sym = def.orig[0];
5990 if (sym instanceof AST_SymbolFunarg || sym instanceof AST_SymbolLambda) return def;
5991 }
5992 } : function(name) {
5993 return find(name) || fn.variables.get(name);
5994 });
5995 }
5996 }
5997 return compose(destructured, level + 2, find);
5998 }
5999 return compose(parent, level + 1, parent.variables ? function(name) {
6000 return find(name) || parent.variables.get(name);
6001 } : find);
6002 }
6003 };
6004 var for_ins = Object.create(null);
6005 var in_use = [];
6006 var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
6007 var value_read = Object.create(null);
6008 var value_modified = Object.create(null);
6009 var var_defs = Object.create(null);
6010 if (self instanceof AST_Toplevel && compressor.top_retain) {
6011 self.variables.each(function(def) {
6012 if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
6013 in_use_ids[def.id] = true;
6014 in_use.push(def);
6015 }
6016 });
6017 }
6018 var assignments = new Dictionary();
6019 var initializations = new Dictionary();
6020 // pass 1: find out which symbols are directly used in
6021 // this scope (not in nested scopes).
6022 var scope = this;
6023 var tw = new TreeWalker(function(node, descend) {
6024 if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) {
6025 node.each_argname(function(argname) {
6026 var def = argname.definition();
6027 if (!(def.id in in_use_ids)) {
6028 in_use_ids[def.id] = true;
6029 in_use.push(def);
6030 }
6031 });
6032 }
6033 if (node === self) return;
6034 if (scope === self) {
6035 if (node instanceof AST_DefClass) {
6036 var def = node.name.definition();
6037 if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6038 in_use_ids[def.id] = true;
6039 in_use.push(def);
6040 }
6041 if (node.extends) node.extends.walk(tw);
6042 var is_export = false;
6043 if (tw.parent() instanceof AST_ExportDefault) {
6044 is_export = true;
6045 export_defaults[def.id] = true;
6046 }
6047 node.properties.forEach(function(prop) {
6048 if (prop.key instanceof AST_Node) prop.key.walk(tw);
6049 if (!prop.value) return;
6050 if (is_export || prop instanceof AST_ClassField && prop.static) {
6051 var save_scope = scope;
6052 scope = node;
6053 prop.value.walk(tw);
6054 scope = save_scope;
6055 } else {
6056 initializations.add(def.id, prop.value);
6057 }
6058 });
6059 return true;
6060 }
6061 if (node instanceof AST_LambdaDefinition) {
6062 var def = node.name.definition();
6063 if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6064 in_use_ids[def.id] = true;
6065 in_use.push(def);
6066 }
6067 initializations.add(def.id, node);
6068 if (tw.parent() instanceof AST_ExportDefault) {
6069 export_defaults[def.id] = true;
6070 } else {
6071 return true;
6072 }
6073 }
6074 if (node instanceof AST_Definitions) {
6075 node.definitions.forEach(function(defn) {
6076 var value = defn.value;
6077 var side_effects = value
6078 && (defn.name instanceof AST_Destructured || value.has_side_effects(compressor));
6079 var shared = side_effects && value.tail_node().operator == "=";
6080 defn.name.mark_symbol(function(name) {
6081 if (!(name instanceof AST_SymbolDeclaration)) return;
6082 var def = name.definition();
6083 var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6084 if (node instanceof AST_Var && def.orig[0] instanceof AST_SymbolCatch) {
6085 var redef = def.redefined();
6086 if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1;
6087 }
6088 if (!(def.id in in_use_ids) && (!drop_vars || def.exported
6089 || (node instanceof AST_Const ? def.redefined() : def.const_redefs)
6090 || !(node instanceof AST_Var || is_safe_lexical(def)))) {
6091 in_use_ids[def.id] = true;
6092 in_use.push(def);
6093 }
6094 if (value) {
6095 if (!side_effects) {
6096 initializations.add(def.id, value);
6097 } else if (shared) {
6098 verify_safe_usage(def, name, value_modified[def.id]);
6099 }
6100 assignments.add(def.id, defn);
6101 }
6102 return true;
6103 }, tw);
6104 if (side_effects) value.walk(tw);
6105 });
6106 return true;
6107 }
6108 if (node instanceof AST_SymbolFunarg) {
6109 var def = node.definition();
6110 var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6111 assignments.add(def.id, node);
6112 return true;
6113 }
6114 if (node instanceof AST_SymbolImport) {
6115 var def = node.definition();
6116 if (!(def.id in in_use_ids) && (!drop_vars || !is_safe_lexical(def))) {
6117 in_use_ids[def.id] = true;
6118 in_use.push(def);
6119 }
6120 return true;
6121 }
6122 } else if (node instanceof AST_This && scope instanceof AST_DefClass) {
6123 var def = scope.name.definition();
6124 if (!(def.id in in_use_ids)) {
6125 in_use_ids[def.id] = true;
6126 in_use.push(def);
6127 }
6128 }
6129 return scan_ref_scoped(node, descend, true);
6130 });
6131 tw.directives = Object.create(compressor.directives);
6132 self.walk(tw);
6133 var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie8") ? function(def) {
6134 return !compressor.exposed(def) && def.references.length == def.replaced;
6135 } : function(def) {
6136 if (!(def.id in in_use_ids)) return true;
6137 if (def.orig.length - def.eliminated < 2) return false;
6138 // function argument will always overshadow its name
6139 if (def.orig[1] instanceof AST_SymbolFunarg) return true;
6140 // retain if referenced within destructured object of argument
6141 return all(def.references, function(ref) {
6142 return !ref.in_arg;
6143 });
6144 };
6145 if (compressor.option("ie8")) initializations.each(function(init, id) {
6146 if (id in in_use_ids) return;
6147 init.forEach(function(init) {
6148 init.walk(new TreeWalker(function(node) {
6149 if (node instanceof AST_Function && node.name && !drop_fn_name(node.name.definition())) {
6150 node.walk(tw);
6151 return true;
6152 }
6153 if (node instanceof AST_Scope) return true;
6154 }));
6155 });
6156 });
6157 // pass 2: for every used symbol we need to walk its
6158 // initialization code to figure out if it uses other
6159 // symbols (that may not be in_use).
6160 tw = new TreeWalker(scan_ref_scoped);
6161 for (var i = 0; i < in_use.length; i++) {
6162 var init = initializations.get(in_use[i].id);
6163 if (init) init.forEach(function(init) {
6164 init.walk(tw);
6165 });
6166 }
6167 Object.keys(assign_in_use).forEach(function(id) {
6168 var assigns = assign_in_use[id];
6169 if (!assigns) {
6170 delete assign_in_use[id];
6171 return;
6172 }
6173 assigns = assigns.reduce(function(in_use, assigns) {
6174 assigns.forEach(function(assign) {
6175 push_uniq(in_use, assign);
6176 });
6177 return in_use;
6178 }, []);
6179 var in_use = (assignments.get(id) || []).filter(function(node) {
6180 return find_if(node instanceof AST_Unary ? function(assign) {
6181 return assign === node;
6182 } : function(assign) {
6183 if (assign === node) return true;
6184 if (assign instanceof AST_Unary) return false;
6185 return get_rvalue(assign) === get_rvalue(node);
6186 }, assigns);
6187 });
6188 if (assigns.length == in_use.length) {
6189 assign_in_use[id] = in_use;
6190 } else {
6191 delete assign_in_use[id];
6192 }
6193 });
6194 // pass 3: we should drop declarations not in_use
6195 var trim_defns = [];
6196 var unused_fn_names = [];
6197 var calls_to_drop_args = [];
6198 var fns_with_marked_args = [];
6199 var trimmer = new TreeTransformer(function(node) {
6200 if (node instanceof AST_DefaultValue) return trim_default(trimmer, node);
6201 if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer);
6202 if (node instanceof AST_DestructuredArray) {
6203 var trim = !node.rest;
6204 for (var i = node.elements.length; --i >= 0;) {
6205 var element = node.elements[i].transform(trimmer);
6206 if (element) {
6207 node.elements[i] = element;
6208 trim = false;
6209 } else if (trim) {
6210 node.elements.pop();
6211 } else {
6212 node.elements[i] = make_node(AST_Hole, node.elements[i]);
6213 }
6214 }
6215 return node;
6216 }
6217 if (node instanceof AST_DestructuredObject) {
6218 var properties = [];
6219 node.properties.forEach(function(prop) {
6220 var retain = false;
6221 if (prop.key instanceof AST_Node) {
6222 prop.key = prop.key.transform(tt);
6223 retain = prop.key.has_side_effects(compressor);
6224 }
6225 if ((retain || node.rest) && is_decl(prop.value)) {
6226 prop.value = prop.value.transform(tt);
6227 properties.push(prop);
6228 } else {
6229 var value = prop.value.transform(trimmer);
6230 if (!value && node.rest) {
6231 if (prop.value instanceof AST_DestructuredArray) {
6232 value = make_node(AST_DestructuredArray, prop.value, { elements: [] });
6233 } else {
6234 value = make_node(AST_DestructuredObject, prop.value, { properties: [] });
6235 }
6236 }
6237 if (value) {
6238 prop.value = value;
6239 properties.push(prop);
6240 }
6241 }
6242 });
6243 node.properties = properties;
6244 return node;
6245 }
6246 if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
6247 });
6248 var tt = new TreeTransformer(function(node, descend, in_list) {
6249 var parent = tt.parent();
6250 if (drop_vars) {
6251 var props = [], sym = assign_as_unused(node, props);
6252 if (sym) {
6253 var value;
6254 if (can_drop_lhs(sym, node)) {
6255 if (node instanceof AST_Assign) {
6256 value = get_rhs(node);
6257 if (node.write_only === true) value = value.drop_side_effect_free(compressor);
6258 }
6259 if (!value) value = make_node(AST_Number, node, { value: 0 });
6260 }
6261 if (value) {
6262 if (props.assign) {
6263 var assign = props.assign.drop_side_effect_free(compressor);
6264 if (assign) props.unshift(assign);
6265 }
6266 if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
6267 value = value.drop_side_effect_free(compressor);
6268 }
6269 if (value) props.push(value);
6270 switch (props.length) {
6271 case 0:
6272 return List.skip;
6273 case 1:
6274 return maintain_this_binding(compressor, parent, node, props[0].transform(tt));
6275 default:
6276 return make_sequence(node, props.map(function(prop) {
6277 return prop.transform(tt);
6278 }));
6279 }
6280 }
6281 } else if (node instanceof AST_UnaryPostfix
6282 && node.expression instanceof AST_SymbolRef
6283 && indexOf_assign(node.expression.definition(), node) < 0) {
6284 return make_node(AST_UnaryPrefix, node, {
6285 operator: "+",
6286 expression: node.expression
6287 });
6288 }
6289 }
6290 if (node instanceof AST_Call) calls_to_drop_args.push(node);
6291 if (scope !== self) return;
6292 if (drop_funcs && node !== self && node instanceof AST_DefClass) {
6293 var def = node.name.definition();
6294 if (!(def.id in in_use_ids)) {
6295 log(node.name, "Dropping unused class {name}");
6296 def.eliminated++;
6297 descend(node, tt);
6298 if (parent instanceof AST_ExportDefault) return to_class_expr(node, true);
6299 var trimmed = node.drop_side_effect_free(compressor, true);
6300 if (trimmed === node) trimmed = to_class_expr(node, true);
6301 if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed });
6302 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6303 }
6304 }
6305 if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) {
6306 unused_fn_names.push(node);
6307 }
6308 if (node instanceof AST_Lambda) {
6309 if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
6310 var def = node.name.definition();
6311 if (!(def.id in in_use_ids)) {
6312 log(node.name, "Dropping unused function {name}");
6313 def.eliminated++;
6314 if (parent instanceof AST_ExportDefault) {
6315 descend_scope();
6316 return to_func_expr(node, true);
6317 }
6318 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6319 }
6320 }
6321 if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
6322 unused_fn_names.push(node);
6323 }
6324 if (!(node instanceof AST_Accessor)) {
6325 if (node.rest) {
6326 node.rest = node.rest.transform(trimmer);
6327 if (!(node.uses_arguments && !tt.has_directive("use strict"))
6328 && (node.rest instanceof AST_DestructuredArray && node.rest.elements.length == 0
6329 || node.rest instanceof AST_DestructuredObject && node.rest.properties.length == 0)) {
6330 node.rest = null;
6331 }
6332 }
6333 var argnames = node.argnames;
6334 var trim = compressor.drop_fargs(node, parent) && !node.rest;
6335 var default_length = trim ? -1 : node.length();
6336 for (var i = argnames.length; --i >= 0;) {
6337 var sym = argnames[i];
6338 if (!(sym instanceof AST_SymbolFunarg)) {
6339 var arg = sym.transform(trimmer);
6340 if (arg) {
6341 trim = false;
6342 } else if (trim) {
6343 log(sym.name, "Dropping unused default argument {name}");
6344 argnames.pop();
6345 } else if (i > default_length) {
6346 log(sym.name, "Dropping unused default argument assignment {name}");
6347 sym.name.__unused = true;
6348 argnames[i] = sym.name;
6349 } else {
6350 log(sym.name, "Dropping unused default argument value {name}");
6351 sym.value = make_node(AST_Number, sym, { value: 0 });
6352 }
6353 continue;
6354 }
6355 var def = sym.definition();
6356 if (def.id in in_use_ids) {
6357 trim = false;
6358 if (indexOf_assign(def, sym) < 0) sym.__unused = null;
6359 } else if (trim) {
6360 log(sym, "Dropping unused function argument {name}");
6361 argnames.pop();
6362 } else {
6363 sym.__unused = true;
6364 }
6365 }
6366 fns_with_marked_args.push(node);
6367 }
6368 }
6369 if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
6370 node.argname.transform(trimmer);
6371 }
6372 if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) {
6373 // place uninitialized names at the start
6374 var body = [], head = [], tail = [];
6375 // for unused names whose initialization has
6376 // side effects, we can cascade the init. code
6377 // into the next one, or next statement.
6378 var side_effects = [];
6379 var duplicated = 0;
6380 var is_var = node instanceof AST_Var;
6381 node.definitions.forEach(function(def) {
6382 if (def.value) def.value = def.value.transform(tt);
6383 var value = def.value;
6384 if (def.name instanceof AST_Destructured) {
6385 var name = trim_destructured(def.name, value, function(node) {
6386 if (!drop_vars) return node;
6387 if (node.definition().id in in_use_ids) return node;
6388 if (is_catch(node)) return node;
6389 if (is_var && !can_drop_symbol(node)) return node;
6390 return null;
6391 });
6392 if (name) {
6393 flush();
6394 } else {
6395 value = value.drop_side_effect_free(compressor);
6396 if (value) side_effects.push(value);
6397 }
6398 return;
6399 }
6400 var sym = def.name.definition();
6401 var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym);
6402 if (!drop_sym || !drop_vars || sym.id in in_use_ids) {
6403 if (value && indexOf_assign(sym, def) < 0) {
6404 value = value.drop_side_effect_free(compressor);
6405 if (value) {
6406 AST_Node.warn("Side effects in last use of variable {name} [{file}:{line},{col}]", template(def.name));
6407 side_effects.push(value);
6408 }
6409 value = null;
6410 trim_defns.push(def);
6411 }
6412 var old_def;
6413 if (!value && !(node instanceof AST_Let)) {
6414 if (parent instanceof AST_ExportDeclaration) {
6415 flush();
6416 } else if (drop_sym && var_defs[sym.id] > 1) {
6417 AST_Node.info("Dropping declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6418 var_defs[sym.id]--;
6419 sym.eliminated++;
6420 } else {
6421 head.push(def);
6422 }
6423 } else if (compressor.option("functions")
6424 && !compressor.option("ie8")
6425 && node instanceof AST_Var
6426 && var_defs[sym.id] == 1
6427 && sym.assignments == 0
6428 && value instanceof AST_LambdaExpression
6429 && !is_arguments(sym)
6430 && !is_arrow(value)
6431 && assigned_once(value, sym.references)
6432 && can_declare_defun(value)
6433 && (old_def = rename_def(value, def.name.name)) !== false) {
6434 AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
6435 var ctor;
6436 switch (value.CTOR) {
6437 case AST_AsyncFunction:
6438 ctor = AST_AsyncDefun;
6439 break;
6440 case AST_AsyncGeneratorFunction:
6441 ctor = AST_AsyncGeneratorDefun;
6442 break;
6443 case AST_Function:
6444 ctor = AST_Defun;
6445 break;
6446 case AST_GeneratorFunction:
6447 ctor = AST_GeneratorDefun;
6448 break;
6449 }
6450 var defun = make_node(ctor, def, value);
6451 defun.name = make_node(AST_SymbolDefun, def.name, def.name);
6452 var name_def = def.name.scope.resolve().def_function(defun.name);
6453 if (old_def) old_def.forEach(function(node) {
6454 node.name = name_def.name;
6455 node.thedef = name_def;
6456 node.reference();
6457 });
6458 body.push(defun);
6459 } else {
6460 if (drop_sym
6461 && var_defs[sym.id] > 1
6462 && !(parent instanceof AST_ExportDeclaration)
6463 && sym.orig.indexOf(def.name) > sym.eliminated) {
6464 var_defs[sym.id]--;
6465 duplicated++;
6466 }
6467 flush();
6468 }
6469 } else if (is_catch(def.name)) {
6470 value = value && value.drop_side_effect_free(compressor);
6471 if (value) side_effects.push(value);
6472 if (var_defs[sym.id] > 1) {
6473 AST_Node.warn("Dropping duplicated declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6474 var_defs[sym.id]--;
6475 sym.eliminated++;
6476 } else {
6477 def.value = null;
6478 head.push(def);
6479 }
6480 } else {
6481 value = value && !value.single_use && value.drop_side_effect_free(compressor);
6482 if (value) {
6483 AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
6484 side_effects.push(value);
6485 } else {
6486 log(def.name, "Dropping unused variable {name}");
6487 }
6488 sym.eliminated++;
6489 }
6490
6491 function assigned_once(fn, refs) {
6492 if (refs.length == 0) return fn === def.name.fixed_value();
6493 return all(refs, function(ref) {
6494 return fn === ref.fixed_value();
6495 });
6496 }
6497
6498 function can_declare_defun(fn) {
6499 if (compressor.has_directive("use strict") || !(fn instanceof AST_Function)) {
6500 return parent instanceof AST_Scope;
6501 }
6502 return parent instanceof AST_Block
6503 || parent instanceof AST_For && parent.init === node
6504 || parent instanceof AST_If;
6505 }
6506
6507 function rename_def(fn, name) {
6508 if (!fn.name) return null;
6509 var def = fn.name.definition();
6510 if (def.orig.length > 1) return null;
6511 if (def.assignments > 0) return false;
6512 if (def.name == name) return def;
6513 if (name == "await" && is_async(fn)) return false;
6514 if (name == "yield" && is_generator(fn)) return false;
6515 return all(def.references, function(ref) {
6516 return ref.scope.find_variable(name) === sym;
6517 }) && def;
6518 }
6519
6520 function is_catch(node) {
6521 var sym = node.definition();
6522 return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve();
6523 }
6524
6525 function flush() {
6526 if (side_effects.length > 0) {
6527 if (tail.length == 0) {
6528 body.push(make_node(AST_SimpleStatement, node, {
6529 body: make_sequence(node, side_effects)
6530 }));
6531 } else if (value) {
6532 side_effects.push(value);
6533 def.value = make_sequence(value, side_effects);
6534 } else {
6535 def.value = make_node(AST_UnaryPrefix, def, {
6536 operator: "void",
6537 expression: make_sequence(def, side_effects)
6538 });
6539 }
6540 side_effects = [];
6541 }
6542 tail.push(def);
6543 }
6544 });
6545 switch (head.length) {
6546 case 0:
6547 if (tail.length == 0) break;
6548 if (tail.length == duplicated) {
6549 [].unshift.apply(side_effects, tail.map(function(def) {
6550 AST_Node.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
6551 var sym = def.name.definition();
6552 var ref = make_node(AST_SymbolRef, def.name, def.name);
6553 sym.references.push(ref);
6554 var assign = make_node(AST_Assign, def, {
6555 operator: "=",
6556 left: ref,
6557 right: def.value
6558 });
6559 var index = indexOf_assign(sym, def);
6560 if (index >= 0) assign_in_use[sym.id][index] = assign;
6561 sym.eliminated++;
6562 return assign;
6563 }));
6564 break;
6565 }
6566 case 1:
6567 if (tail.length == 0) {
6568 var id = head[0].name.definition().id;
6569 if (id in for_ins) {
6570 node.definitions = head;
6571 for_ins[id].init = node;
6572 break;
6573 }
6574 }
6575 default:
6576 node.definitions = head.concat(tail);
6577 body.push(node);
6578 }
6579 if (side_effects.length > 0) {
6580 body.push(make_node(AST_SimpleStatement, node, {
6581 body: make_sequence(node, side_effects)
6582 }));
6583 }
6584 return insert_statements(body, node, in_list);
6585 }
6586 if (node instanceof AST_Assign) {
6587 descend(node, tt);
6588 if (node.left instanceof AST_Destructured) {
6589 var lhs = trim_destructured(node.left, node.right, function(node) {
6590 return node;
6591 });
6592 if (!lhs) return node.right;
6593 node.left = lhs;
6594 }
6595 return node;
6596 }
6597 if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
6598 // Certain combination of unused name + side effect leads to invalid AST:
6599 // https://github.com/mishoo/UglifyJS/issues/1830
6600 // We fix it at this stage by moving the label inwards, back to the `for`.
6601 descend(node, tt);
6602 if (node.body instanceof AST_BlockStatement) {
6603 var block = node.body;
6604 node.body = block.body.pop();
6605 block.body.push(node);
6606 return in_list ? List.splice(block.body) : block;
6607 }
6608 return node;
6609 }
6610 if (node instanceof AST_Scope) {
6611 descend_scope();
6612 return node;
6613 }
6614 if (node instanceof AST_SymbolImport) {
6615 if (!compressor.option("imports") || node.definition().id in in_use_ids) return node;
6616 return in_list ? List.skip : null;
6617 }
6618
6619 function descend_scope() {
6620 var save_scope = scope;
6621 scope = node;
6622 descend(node, tt);
6623 scope = save_scope;
6624 }
6625 }, function(node, in_list) {
6626 if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
6627 // Certain combination of unused name + side effect leads to invalid AST:
6628 // https://github.com/mishoo/UglifyJS/issues/44
6629 // https://github.com/mishoo/UglifyJS/issues/1838
6630 // https://github.com/mishoo/UglifyJS/issues/3371
6631 // We fix it at this stage by moving the `var` outside the `for`.
6632 if (node instanceof AST_For) {
6633 var block;
6634 if (node.init instanceof AST_BlockStatement) {
6635 block = node.init;
6636 node.init = block.body.pop();
6637 block.body.push(node);
6638 }
6639 if (node.init instanceof AST_Defun) {
6640 if (!block) {
6641 block = make_node(AST_BlockStatement, node, {
6642 body: [ node ]
6643 });
6644 }
6645 block.body.splice(-1, 0, node.init);
6646 node.init = null;
6647 } else if (node.init instanceof AST_SimpleStatement) {
6648 node.init = node.init.body;
6649 } else if (is_empty(node.init)) {
6650 node.init = null;
6651 }
6652 return !block ? node : in_list ? List.splice(block.body) : block;
6653 }
6654 if (node instanceof AST_ForIn) {
6655 if (!drop_vars || !compressor.option("loops")) return;
6656 if (!is_empty(node.body)) return;
6657 var sym = get_init_symbol(node);
6658 if (!sym) return;
6659 var def = sym.definition();
6660 if (def.id in in_use_ids) return;
6661 log(sym, "Dropping unused loop variable {name}");
6662 if (for_ins[def.id] === node) delete for_ins[def.id];
6663 var body = [];
6664 var value = node.object.drop_side_effect_free(compressor);
6665 if (value) {
6666 AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", value.start);
6667 body.push(make_node(AST_SimpleStatement, node, {
6668 body: value
6669 }));
6670 }
6671 if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) {
6672 body.push(node.init);
6673 }
6674 return insert_statements(body, node, in_list);
6675 }
6676 if (node instanceof AST_Import) {
6677 if (node.properties && node.properties == 0) node.properties = null;
6678 return node;
6679 }
6680 if (node instanceof AST_Sequence) {
6681 if (node.expressions.length > 1) return;
6682 return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
6683 }
6684 });
6685 tt.push(compressor.parent());
6686 self.transform(tt);
6687 if (self instanceof AST_Lambda
6688 && self.body.length == 1
6689 && self.body[0] instanceof AST_Directive
6690 && self.body[0].value == "use strict") {
6691 self.body.length = 0;
6692 }
6693 trim_defns.forEach(function(def) {
6694 def.value = null;
6695 });
6696 unused_fn_names.forEach(function(fn) {
6697 fn.name = null;
6698 });
6699 calls_to_drop_args.forEach(function(call) {
6700 drop_unused_call_args(call, compressor, fns_with_marked_args);
6701 });
6702
6703 function log(sym, text) {
6704 AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym));
6705 }
6706
6707 function template(sym) {
6708 return {
6709 name: sym.name,
6710 file: sym.start.file,
6711 line: sym.start.line,
6712 col : sym.start.col,
6713 };
6714 }
6715
6716 function get_rvalue(expr) {
6717 return expr[expr instanceof AST_Assign ? "right" : "value"];
6718 }
6719
6720 function insert_statements(body, orig, in_list) {
6721 switch (body.length) {
6722 case 0:
6723 return in_list ? List.skip : make_node(AST_EmptyStatement, orig);
6724 case 1:
6725 return body[0];
6726 default:
6727 return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, {
6728 body: body
6729 });
6730 }
6731 }
6732
6733 function track_assigns(def, node) {
6734 if (def.scope !== self) return false;
6735 if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
6736 return assign_in_use[def.id] !== false;
6737 }
6738
6739 function add_assigns(def, node) {
6740 if (!assign_in_use[def.id]) assign_in_use[def.id] = [];
6741 if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns);
6742 }
6743
6744 function indexOf_assign(def, node) {
6745 var nodes = assign_in_use[def.id];
6746 return nodes && nodes.indexOf(node);
6747 }
6748
6749 function verify_safe_usage(def, read, modified) {
6750 if (def.id in in_use_ids) return;
6751 if (read && modified) {
6752 in_use_ids[def.id] = read;
6753 in_use.push(def);
6754 } else {
6755 value_read[def.id] = read;
6756 value_modified[def.id] = modified;
6757 }
6758 }
6759
6760 function can_drop_lhs(sym, node) {
6761 var def = sym.definition();
6762 var in_use = in_use_ids[def.id];
6763 if (!in_use) return true;
6764 if (node[node instanceof AST_Assign ? "left" : "expression"] !== sym) return false;
6765 return in_use === sym && def.references.length - def.replaced == 1 || indexOf_assign(def, node) < 0;
6766 }
6767
6768 function get_rhs(assign) {
6769 var rhs = assign.right;
6770 if (!assign.write_only) return rhs;
6771 if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
6772 if (!(rhs.left instanceof AST_SymbolRef)) return rhs;
6773 if (!(assign.left instanceof AST_SymbolRef)) return rhs;
6774 var def = assign.left.definition();
6775 if (rhs.left.definition() !== def) return rhs;
6776 if (rhs.right.has_side_effects(compressor)) return rhs;
6777 if (track_assigns(def, rhs.left)) add_assigns(def, rhs.left);
6778 return rhs.right;
6779 }
6780
6781 function get_init_symbol(for_in) {
6782 var init = for_in.init;
6783 if (init instanceof AST_Definitions) {
6784 init = init.definitions[0].name;
6785 return init instanceof AST_SymbolDeclaration && init;
6786 }
6787 while (init instanceof AST_PropAccess) init = init.expression.tail_node();
6788 if (init instanceof AST_SymbolRef) return init;
6789 }
6790
6791 function scan_ref_scoped(node, descend, init) {
6792 if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
6793 var def = node.left.definition();
6794 if (def.scope === self) assignments.add(def.id, node);
6795 }
6796 if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
6797 var def = node.expression.definition();
6798 if (def.scope === self) assignments.add(def.id, node);
6799 }
6800 var node_def, props = [], sym = assign_as_unused(node, props);
6801 if (sym && self.variables.get(sym.name) === (node_def = sym.definition())
6802 && !(is_arguments(node_def) && !all(self.argnames, function(argname) {
6803 return !argname.match_symbol(function(node) {
6804 if (node instanceof AST_SymbolFunarg) {
6805 var def = node.definition();
6806 return def.references.length > def.replaced;
6807 }
6808 }, true);
6809 }))) {
6810 if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return;
6811 var assign = props.assign;
6812 if (assign) {
6813 assign.write_only = true;
6814 assign.walk(tw);
6815 assign.write_only = "p";
6816 }
6817 props.forEach(function(prop) {
6818 prop.walk(tw);
6819 });
6820 if (node instanceof AST_Assign) {
6821 var right = get_rhs(node), shared = false;
6822 if (init && node.write_only === true && node_def.scope === self && !right.has_side_effects(compressor)) {
6823 initializations.add(node_def.id, right);
6824 } else {
6825 right.walk(tw);
6826 shared = right.tail_node().operator == "=";
6827 }
6828 if (node.left === sym) {
6829 if (!node.write_only || shared) {
6830 verify_safe_usage(node_def, sym, value_modified[node_def.id]);
6831 }
6832 } else {
6833 var fixed = sym.fixed_value();
6834 if (!fixed || !fixed.is_constant()) {
6835 verify_safe_usage(node_def, value_read[node_def.id], true);
6836 }
6837 }
6838 }
6839 if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
6840 return true;
6841 }
6842 if (node instanceof AST_ForIn) {
6843 if (node.init instanceof AST_SymbolRef && scope === self) {
6844 var id = node.init.definition().id;
6845 if (!(id in for_ins)) for_ins[id] = node;
6846 }
6847 if (!drop_vars || !compressor.option("loops")) return;
6848 if (!is_empty(node.body)) return;
6849 if (node.init.has_side_effects(compressor)) return;
6850 var sym = get_init_symbol(node);
6851 if (!sym) return;
6852 var def = sym.definition();
6853 if (def.scope !== self) {
6854 var d = find_variable(sym.name);
6855 if (d === def || d && d.redefined() === def) return;
6856 }
6857 node.object.walk(tw);
6858 return true;
6859 }
6860 if (node instanceof AST_SymbolRef) {
6861 node_def = node.definition();
6862 if (!(node_def.id in in_use_ids)) {
6863 in_use_ids[node_def.id] = true;
6864 in_use.push(node_def);
6865 }
6866 if (cross_scope(node_def.scope, node.scope)) {
6867 var redef = node_def.redefined();
6868 if (redef && !(redef.id in in_use_ids)) {
6869 in_use_ids[redef.id] = true;
6870 in_use.push(redef);
6871 }
6872 }
6873 if (track_assigns(node_def, node)) add_assigns(node_def, node);
6874 return true;
6875 }
6876 if (node instanceof AST_Scope) {
6877 var save_scope = scope;
6878 scope = node;
6879 descend();
6880 scope = save_scope;
6881 return true;
6882 }
6883 }
6884
6885 function is_decl(node) {
6886 return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration;
6887 }
6888
6889 function trim_default(trimmer, node) {
6890 node.value = node.value.transform(tt);
6891 var name = node.name.transform(trimmer);
6892 if (!name) {
6893 var value = node.value.drop_side_effect_free(compressor);
6894 if (!value) return null;
6895 name = node.name;
6896 if (name instanceof AST_Destructured) {
6897 name = name.clone();
6898 name[name instanceof AST_DestructuredArray ? "elements" : "properties"] = [];
6899 if (!(value instanceof AST_Array || value.is_string(compressor)
6900 || name instanceof AST_DestructuredObject
6901 && (value instanceof AST_Object
6902 || value.is_boolean(compressor)
6903 || value.is_number(compressor)))) {
6904 value = make_node(AST_Array, value, {
6905 elements: [ value ],
6906 });
6907 }
6908 node.name = name;
6909 } else {
6910 log(name, "Side effects in default value of unused variable {name}");
6911 }
6912 node.value = value;
6913 }
6914 return node;
6915 }
6916
6917 function trim_destructured(node, value, process) {
6918 var trimmer = new TreeTransformer(function(node) {
6919 if (node instanceof AST_DefaultValue) {
6920 if (compressor.option("default_values") && value && value.is_defined(compressor)) {
6921 node = node.name;
6922 } else {
6923 return trim_default(trimmer, node);
6924 }
6925 }
6926 if (node instanceof AST_DestructuredArray) {
6927 var save = value;
6928 if (value instanceof AST_SymbolRef) value = value.fixed_value();
6929 var values = value instanceof AST_Array && value.elements;
6930 var elements = [];
6931 node.elements.forEach(function(element, index) {
6932 value = values && values[index];
6933 if (value instanceof AST_Spread) value = values = null;
6934 if (element instanceof AST_Hole) return;
6935 element = element.transform(trimmer);
6936 if (element) elements[index] = element;
6937 });
6938 if (node.rest) {
6939 if (compressor.option("rests")) {
6940 value = values && make_node(AST_Array, save, {
6941 elements: values.slice(node.elements.length),
6942 });
6943 node.rest = node.rest.transform(trimmer);
6944 } else {
6945 node.rest = node.rest.transform(tt);
6946 }
6947 }
6948 value = save;
6949 if (node.rest) {
6950 elements.length = node.elements.length;
6951 } else if (values && elements.length == 0) {
6952 return null;
6953 }
6954 fill_holes(node, elements);
6955 node.elements = elements;
6956 return node;
6957 }
6958 if (node instanceof AST_DestructuredObject) {
6959 var save = value;
6960 if (value instanceof AST_SymbolRef) value = value.fixed_value();
6961 var values;
6962 if (value instanceof AST_Object) {
6963 values = Object.create(null);
6964 for (var i = 0; i < value.properties.length; i++) {
6965 var prop = value.properties[i];
6966 if (typeof prop.key != "string") {
6967 values = null;
6968 break;
6969 }
6970 values[prop.key] = prop.value;
6971 }
6972 }
6973 var properties = [];
6974 node.properties.forEach(function(prop) {
6975 var retain;
6976 if (prop.key instanceof AST_Node) {
6977 prop.key = prop.key.transform(tt);
6978 value = null;
6979 retain = prop.key.has_side_effects(compressor);
6980 } else {
6981 value = values && values[prop.key];
6982 retain = false;
6983 }
6984 if ((retain || node.rest) && is_decl(prop.value)) {
6985 prop.value = prop.value.transform(tt);
6986 properties.push(prop);
6987 } else {
6988 var newValue = prop.value.transform(trimmer);
6989 if (!newValue && node.rest) {
6990 if (prop.value instanceof AST_DestructuredArray) {
6991 newValue = make_node(AST_DestructuredArray, prop.value, { elements: [] });
6992 } else {
6993 newValue = make_node(AST_DestructuredObject, prop.value, { properties: [] });
6994 }
6995 }
6996 if (newValue) {
6997 prop.value = newValue;
6998 properties.push(prop);
6999 }
7000 }
7001 });
7002 if (node.rest) {
7003 if (compressor.option("rests")) {
7004 value = values && make_node(AST_Object, save, {
7005 properties: [],
7006 });
7007 node.rest = node.rest.transform(trimmer);
7008 } else {
7009 node.rest = node.rest.transform(tt);
7010 }
7011 }
7012 value = save;
7013 if (properties.length == 0 && !node.rest && value && !value.may_throw_on_access(compressor)) {
7014 return null;
7015 }
7016 node.properties = properties;
7017 return node;
7018 }
7019 return process(node);
7020 });
7021 return node.transform(trimmer);
7022 }
7023 });
7024
7025 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
7026 if (compressor.has_directive("use asm")) return;
7027 var hoist_funs = compressor.option("hoist_funs");
7028 var hoist_vars = compressor.option("hoist_vars");
7029 var self = this;
7030 if (hoist_vars) {
7031 // let's count var_decl first, we seem to waste a lot of
7032 // space if we hoist `var` when there's only one.
7033 var var_decl = 0;
7034 self.walk(new TreeWalker(function(node) {
7035 if (var_decl > 1) return true;
7036 if (node instanceof AST_ExportDeclaration) return true;
7037 if (node instanceof AST_Scope && node !== self) return true;
7038 if (node instanceof AST_Var) {
7039 var_decl++;
7040 return true;
7041 }
7042 }));
7043 if (var_decl <= 1) hoist_vars = false;
7044 }
7045 if (!hoist_funs && !hoist_vars) return;
7046 var consts = Object.create(null);
7047 var dirs = [];
7048 var hoisted = [];
7049 var vars = new Dictionary(), vars_found = 0;
7050 var tt = new TreeTransformer(function(node, descend, in_list) {
7051 if (node === self) return;
7052 if (node instanceof AST_Directive) {
7053 dirs.push(node);
7054 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7055 }
7056 if (node instanceof AST_LambdaDefinition) {
7057 if (!hoist_funs) return node;
7058 var p = tt.parent();
7059 if (p instanceof AST_ExportDeclaration) return node;
7060 if (p instanceof AST_ExportDefault) return node;
7061 if (p !== self && compressor.has_directive("use strict")) return node;
7062 hoisted.push(node);
7063 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7064 }
7065 if (node instanceof AST_Var) {
7066 if (!hoist_vars) return node;
7067 var p = tt.parent();
7068 if (p instanceof AST_ExportDeclaration) return node;
7069 if (!all(node.definitions, function(defn) {
7070 var sym = defn.name;
7071 return sym instanceof AST_SymbolVar
7072 && !consts[sym.name]
7073 && self.find_variable(sym.name) === sym.definition();
7074 })) return node;
7075 node.definitions.forEach(function(def) {
7076 vars.set(def.name.name, def);
7077 ++vars_found;
7078 });
7079 var seq = node.to_assignments();
7080 if (p instanceof AST_ForEnumeration && p.init === node) {
7081 if (seq) return seq;
7082 var def = node.definitions[0].name;
7083 return make_node(AST_SymbolRef, def, def);
7084 }
7085 if (p instanceof AST_For && p.init === node) return seq;
7086 if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7087 return make_node(AST_SimpleStatement, node, { body: seq });
7088 }
7089 if (node instanceof AST_Scope) return node;
7090 if (node instanceof AST_SymbolConst) {
7091 consts[node.name] = true;
7092 return node;
7093 }
7094 });
7095 self.transform(tt);
7096 if (vars_found > 0) {
7097 // collect only vars which don't show up in self's arguments list
7098 var defs = [];
7099 if (self instanceof AST_Lambda) self.each_argname(function(argname) {
7100 vars.del(argname.name);
7101 });
7102 vars.each(function(def, name) {
7103 def = def.clone();
7104 def.value = null;
7105 defs.push(def);
7106 vars.set(name, def);
7107 });
7108 if (defs.length > 0) {
7109 // try to merge in assignments
7110 insert_vars(self.body);
7111 defs = make_node(AST_Var, self, { definitions: defs });
7112 hoisted.push(defs);
7113 }
7114 }
7115 self.body = dirs.concat(hoisted, self.body);
7116
7117 function insert_vars(body) {
7118 while (body.length) {
7119 var stat = body[0];
7120 if (stat instanceof AST_SimpleStatement) {
7121 var expr = stat.body, sym, assign;
7122 if (expr instanceof AST_Assign
7123 && expr.operator == "="
7124 && (sym = expr.left) instanceof AST_Symbol
7125 && vars.has(sym.name)) {
7126 var def = vars.get(sym.name);
7127 if (def.value) break;
7128 var value = expr.right;
7129 if (value instanceof AST_Sequence) value = value.clone();
7130 def.value = value;
7131 remove(defs, def);
7132 defs.push(def);
7133 body.shift();
7134 continue;
7135 }
7136 if (expr instanceof AST_Sequence
7137 && (assign = expr.expressions[0]) instanceof AST_Assign
7138 && assign.operator == "="
7139 && (sym = assign.left) instanceof AST_Symbol
7140 && vars.has(sym.name)) {
7141 var def = vars.get(sym.name);
7142 if (def.value) break;
7143 def.value = assign.right;
7144 remove(defs, def);
7145 defs.push(def);
7146 stat.body = make_sequence(expr, expr.expressions.slice(1));
7147 continue;
7148 }
7149 }
7150 if (stat instanceof AST_EmptyStatement) {
7151 body.shift();
7152 continue;
7153 }
7154 if (stat instanceof AST_BlockStatement && !insert_vars(stat.body)) {
7155 body.shift();
7156 continue;
7157 }
7158 break;
7159 }
7160 return body.length;
7161 }
7162 });
7163
7164 function scan_local_returns(fn, transform) {
7165 fn.walk(new TreeWalker(function(node) {
7166 if (node instanceof AST_Return) {
7167 transform(node);
7168 return true;
7169 }
7170 if (node instanceof AST_Scope && node !== fn) return true;
7171 }));
7172 }
7173
7174 function map_bool_returns(fn) {
7175 var map = Object.create(null);
7176 scan_local_returns(fn, function(node) {
7177 var value = node.value;
7178 if (value) value = value.tail_node();
7179 if (value instanceof AST_SymbolRef) {
7180 var id = value.definition().id;
7181 map[id] = (map[id] || 0) + 1;
7182 }
7183 });
7184 return map;
7185 }
7186
7187 function all_bool(def, bool_returns, compressor) {
7188 return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length - def.replaced
7189 && !compressor.exposed(def);
7190 }
7191
7192 function process_boolean_returns(fn, compressor) {
7193 scan_local_returns(fn, function(node) {
7194 node.in_bool = true;
7195 var value = node.value;
7196 if (value) {
7197 var ev = value.is_truthy() || value.evaluate(compressor, true);
7198 if (!ev) {
7199 value = value.drop_side_effect_free(compressor);
7200 node.value = value ? make_sequence(node.value, [
7201 value,
7202 make_node(AST_Number, node.value, { value: 0 }),
7203 ]) : null;
7204 } else if (!(ev instanceof AST_Node)) {
7205 value = value.drop_side_effect_free(compressor);
7206 node.value = value ? make_sequence(node.value, [
7207 value,
7208 make_node(AST_Number, node.value, { value: 1 }),
7209 ]) : make_node(AST_Number, node.value, { value: 1 });
7210 }
7211 }
7212 });
7213 }
7214
7215 AST_Scope.DEFMETHOD("process_boolean_returns", noop);
7216 AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
7217 if (!compressor.option("booleans")) return;
7218 var bool_returns = map_bool_returns(this);
7219 if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
7220 if (compressor.parent() instanceof AST_ExportDefault) return;
7221 process_boolean_returns(this, compressor);
7222 });
7223 AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
7224 if (!compressor.option("booleans")) return;
7225 var bool_returns = map_bool_returns(this);
7226 if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
7227 var parent = compressor.parent();
7228 if (parent instanceof AST_Assign) {
7229 if (parent.operator != "=") return;
7230 var sym = parent.left;
7231 if (!(sym instanceof AST_SymbolRef)) return;
7232 if (!all_bool(sym.definition(), bool_returns, compressor)) return;
7233 } else if (parent instanceof AST_Call && parent.expression !== this) {
7234 var exp = parent.expression;
7235 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
7236 if (!(exp instanceof AST_Lambda)) return;
7237 if (exp.uses_arguments || exp.pinned()) return;
7238 var sym = exp.argnames[parent.args.indexOf(this)];
7239 if (sym instanceof AST_DefaultValue) sym = sym.name;
7240 if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
7241 } else if (parent.TYPE == "Call") {
7242 compressor.pop();
7243 var in_bool = compressor.in_boolean_context();
7244 compressor.push(this);
7245 if (!in_bool) return;
7246 } else return;
7247 process_boolean_returns(this, compressor);
7248 });
7249
7250 AST_BlockScope.DEFMETHOD("var_names", function() {
7251 var var_names = this._var_names;
7252 if (!var_names) {
7253 this._var_names = var_names = Object.create(null);
7254 this.enclosed.forEach(function(def) {
7255 var_names[def.name] = true;
7256 });
7257 this.variables.each(function(def, name) {
7258 var_names[name] = true;
7259 });
7260 }
7261 return var_names;
7262 });
7263
7264 AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
7265 var scopes = [ this ];
7266 if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
7267 var s = ref.scope;
7268 if (member(s, scopes)) return;
7269 do {
7270 push_uniq(scopes, s);
7271 s = s.parent_scope;
7272 } while (s && s !== this);
7273 });
7274 prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
7275 var name = prefix;
7276 for (var i = 0; !all(scopes, function(scope) {
7277 return !scope.var_names()[name];
7278 }); i++) name = prefix + "$" + i;
7279 var sym = make_node(type, orig, {
7280 name: name,
7281 scope: this,
7282 });
7283 var def = this.def_variable(sym);
7284 scopes.forEach(function(scope) {
7285 scope.enclosed.push(def);
7286 scope.var_names()[name] = true;
7287 });
7288 return sym;
7289 });
7290
7291 AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
7292 if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
7293 var self = this;
7294 var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
7295 var defs_by_id = Object.create(null);
7296 self.transform(new TreeTransformer(function(node, descend) {
7297 if (node instanceof AST_Assign) {
7298 if (node.operator != "=") return;
7299 if (!node.write_only) return;
7300 if (node.left.scope !== self) return;
7301 if (!can_hoist(node.left, node.right, 1)) return;
7302 descend(node, this);
7303 var defs = new Dictionary();
7304 var assignments = [];
7305 var decls = [];
7306 node.right.properties.forEach(function(prop) {
7307 var decl = make_sym(node.left, prop.key);
7308 decls.push(make_node(AST_VarDef, node, {
7309 name: decl,
7310 value: null
7311 }));
7312 var sym = make_node(AST_SymbolRef, node, {
7313 name: decl.name,
7314 scope: self,
7315 thedef: decl.definition()
7316 });
7317 sym.reference();
7318 assignments.push(make_node(AST_Assign, node, {
7319 operator: "=",
7320 left: sym,
7321 right: prop.value
7322 }));
7323 });
7324 defs_by_id[node.left.definition().id] = defs;
7325 self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
7326 definitions: decls
7327 }));
7328 return make_sequence(node, assignments);
7329 }
7330 if (node instanceof AST_Scope) return node === self ? undefined : node;
7331 if (node instanceof AST_VarDef) {
7332 if (!can_hoist(node.name, node.value, 0)) return;
7333 descend(node, this);
7334 var defs = new Dictionary();
7335 var var_defs = [];
7336 node.value.properties.forEach(function(prop) {
7337 var_defs.push(make_node(AST_VarDef, node, {
7338 name: make_sym(node.name, prop.key),
7339 value: prop.value
7340 }));
7341 });
7342 defs_by_id[node.name.definition().id] = defs;
7343 return List.splice(var_defs);
7344 }
7345
7346 function make_sym(sym, key) {
7347 var new_var = self.make_var(AST_SymbolVar, sym, sym.name + "_" + key);
7348 defs.set(key, new_var.definition());
7349 return new_var;
7350 }
7351 }));
7352 self.transform(new TreeTransformer(function(node, descend) {
7353 if (node instanceof AST_Binary) return replace("right");
7354 if (node instanceof AST_PropAccess) {
7355 if (!(node.expression instanceof AST_SymbolRef)) return;
7356 var defs = defs_by_id[node.expression.definition().id];
7357 if (!defs) return;
7358 var def = defs.get(node.getProperty());
7359 var sym = make_node(AST_SymbolRef, node, {
7360 name: def.name,
7361 scope: node.expression.scope,
7362 thedef: def
7363 });
7364 sym.reference();
7365 return sym;
7366 }
7367 if (node instanceof AST_Unary) {
7368 if (unary_side_effects[node.operator]) return;
7369 return replace("expression");
7370 }
7371
7372 function replace(prop) {
7373 var sym = node[prop];
7374 if (!(sym instanceof AST_SymbolRef)) return;
7375 if (!(sym.definition().id in defs_by_id)) return;
7376 var opt = node.clone();
7377 opt[prop] = make_node(AST_Object, sym, {
7378 properties: []
7379 });
7380 return opt;
7381 }
7382 }));
7383
7384 function can_hoist(sym, right, count) {
7385 if (!(sym instanceof AST_Symbol)) return;
7386 var def = sym.definition();
7387 if (def.assignments != count) return;
7388 if (def.direct_access) return;
7389 if (def.escaped.depth == 1) return;
7390 if (def.references.length - def.replaced == count) return;
7391 if (def.single_use) return;
7392 if (top_retain(def)) return;
7393 if (sym.fixed_value() !== right) return;
7394 return right instanceof AST_Object
7395 && right.properties.length > 0
7396 && all(right.properties, can_hoist_property)
7397 && all(def.references, function(ref) {
7398 return ref.fixed_value() === right;
7399 })
7400 && can_drop_symbol(sym);
7401 }
7402 });
7403
7404 function fn_name_unused(fn, compressor) {
7405 if (!fn.name || !compressor.option("ie8")) return true;
7406 var def = fn.name.definition();
7407 if (compressor.exposed(def)) return false;
7408 return all(def.references, function(sym) {
7409 return !(sym instanceof AST_SymbolRef);
7410 });
7411 }
7412
7413 // drop_side_effect_free()
7414 // remove side-effect-free parts which only affects return value
7415 (function(def) {
7416 // Drop side-effect-free elements from an array of expressions.
7417 // Returns an array of expressions with side-effects or null
7418 // if all elements were dropped. Note: original array may be
7419 // returned if nothing changed.
7420 function trim(nodes, compressor, first_in_statement, spread) {
7421 var len = nodes.length;
7422 var ret = [], changed = false;
7423 for (var i = 0; i < len; i++) {
7424 var node = nodes[i];
7425 var trimmed;
7426 if (spread && node instanceof AST_Spread) {
7427 trimmed = spread(node, compressor, first_in_statement);
7428 } else {
7429 trimmed = node.drop_side_effect_free(compressor, first_in_statement);
7430 }
7431 if (trimmed !== node) changed = true;
7432 if (trimmed) {
7433 ret.push(trimmed);
7434 first_in_statement = false;
7435 }
7436 }
7437 return ret.length ? changed ? ret : nodes : null;
7438 }
7439 function array_spread(node, compressor, first_in_statement) {
7440 var exp = node.expression;
7441 if (!exp.is_string(compressor)) return node;
7442 return exp.drop_side_effect_free(compressor, first_in_statement);
7443 }
7444 function convert_spread(node) {
7445 return node instanceof AST_Spread ? make_node(AST_Array, node, {
7446 elements: [ node ]
7447 }) : node;
7448 }
7449 def(AST_Node, return_this);
7450 def(AST_Accessor, return_null);
7451 def(AST_Array, function(compressor, first_in_statement) {
7452 var values = trim(this.elements, compressor, first_in_statement, array_spread);
7453 if (!values) return null;
7454 if (values === this.elements && all(values, function(node) {
7455 return node instanceof AST_Spread;
7456 })) return this;
7457 return make_sequence(this, values.map(convert_spread));
7458 });
7459 def(AST_Assign, function(compressor) {
7460 var left = this.left;
7461 if (left instanceof AST_PropAccess) {
7462 var expr = left.expression;
7463 if (expr.may_throw_on_access(compressor, true)) return this;
7464 if (compressor.has_directive("use strict") && expr.is_constant()) return this;
7465 }
7466 if (left.has_side_effects(compressor)) return this;
7467 var right = this.right;
7468 if (!lazy_op[this.operator.slice(0, -1)]) {
7469 this.write_only = true;
7470 if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
7471 return right.drop_side_effect_free(compressor);
7472 }
7473 }
7474 return this;
7475 });
7476 def(AST_Await, function(compressor) {
7477 if (!compressor.option("awaits")) return this;
7478 var exp = this.expression;
7479 if (!is_primitive(compressor, exp)) return this;
7480 var node = this.clone();
7481 node.expression = exp.drop_side_effect_free(compressor) || make_node(AST_Number, this, { value: 0 });
7482 return node;
7483 });
7484 def(AST_Binary, function(compressor, first_in_statement) {
7485 var left = this.left;
7486 var right = this.right;
7487 var op = this.operator;
7488 if (op == "in" && !is_object(right)) {
7489 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7490 if (lhs === left) return this;
7491 var node = this.clone();
7492 node.left = lhs || make_node(AST_Number, left, { value: 0 });
7493 return node;
7494 }
7495 var rhs = right.drop_side_effect_free(compressor, first_in_statement);
7496 if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement);
7497 if (lazy_op[op] && rhs.has_side_effects(compressor)) {
7498 var node = this;
7499 if (rhs !== right) {
7500 node = node.clone();
7501 node.right = rhs.drop_side_effect_free(compressor);
7502 }
7503 if (op == "??") return node;
7504 var negated = make_node(AST_Binary, this, {
7505 operator: op == "&&" ? "||" : "&&",
7506 left: left.negate(compressor, first_in_statement),
7507 right: node.right,
7508 });
7509 return first_in_statement ? best_of_statement(node, negated) : best_of_expression(node, negated);
7510 }
7511 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7512 if (!lhs) return rhs;
7513 rhs = rhs.drop_side_effect_free(compressor);
7514 if (!rhs) return lhs;
7515 return make_sequence(this, [ lhs, rhs ]);
7516 });
7517 function drop_returns(compressor, exp) {
7518 var drop_body = false;
7519 if (compressor.option("arrows") && is_arrow(exp)) {
7520 if (!exp.value) {
7521 drop_body = true;
7522 } else if (!is_async(exp) || is_primitive(compressor, exp.value)) {
7523 exp.value = exp.value.drop_side_effect_free(compressor);
7524 }
7525 } else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
7526 if (exp.name) {
7527 var def = exp.name.definition();
7528 drop_body = def.references.length == def.replaced;
7529 } else {
7530 drop_body = true;
7531 }
7532 }
7533 if (drop_body) {
7534 var async = is_async(exp);
7535 exp.process_expression(false, function(node) {
7536 var value = node.value;
7537 if (value) {
7538 if (async && !is_primitive(compressor, value)) return node;
7539 value = value.drop_side_effect_free(compressor, true);
7540 }
7541 if (!value) return make_node(AST_EmptyStatement, node);
7542 return make_node(AST_SimpleStatement, node, { body: value });
7543 });
7544 scan_local_returns(exp, function(node) {
7545 var value = node.value;
7546 if (value) {
7547 if (async && !is_primitive(compressor, value)) return;
7548 node.value = value.drop_side_effect_free(compressor);
7549 }
7550 });
7551 }
7552 return drop_body;
7553 }
7554 def(AST_Call, function(compressor, first_in_statement) {
7555 var self = this;
7556 if (self.is_expr_pure(compressor)) {
7557 if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
7558 var args = trim(self.args, compressor, first_in_statement, array_spread);
7559 return args && make_sequence(self, args.map(convert_spread));
7560 }
7561 var exp = self.expression;
7562 if (self.is_call_pure(compressor)) {
7563 var exprs = self.args.slice();
7564 exprs.unshift(exp.expression);
7565 exprs = trim(exprs, compressor, first_in_statement, array_spread);
7566 return exprs && make_sequence(self, exprs.map(convert_spread));
7567 }
7568 if (compressor.option("yields") && is_generator(exp)) {
7569 var call = self.clone();
7570 call.expression = make_node(AST_Function, exp, exp);
7571 call.expression.body = [];
7572 var opt = call.transform(compressor);
7573 if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
7574 }
7575 if (drop_returns(compressor, exp)) {
7576 // always shallow clone to ensure stripping of negated IIFEs
7577 self = self.clone();
7578 self.expression = exp.clone();
7579 // avoid extraneous traversal
7580 if (exp._squeezed) self.expression._squeezed = true;
7581 }
7582 if (self instanceof AST_New) {
7583 var fn = exp;
7584 if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
7585 if (fn instanceof AST_Lambda) {
7586 fn.new = true;
7587 var assign_this_only = all(fn.body, function(stat) {
7588 return !stat.has_side_effects(compressor);
7589 });
7590 delete fn.new;
7591 if (assign_this_only) {
7592 var exprs = self.args.slice();
7593 exprs.unshift(exp);
7594 exprs = trim(exprs, compressor, first_in_statement, array_spread);
7595 return exprs && make_sequence(self, exprs.map(convert_spread));
7596 }
7597 if (!fn.contains_this()) return make_node(AST_Call, self, self);
7598 }
7599 }
7600 return self;
7601 });
7602 def(AST_Class, function(compressor, first_in_statement) {
7603 var exprs = [], values = [];
7604 var props = this.properties;
7605 for (var i = 0; i < props.length; i++) {
7606 var prop = props[i];
7607 if (prop.key instanceof AST_Node) exprs.push(prop.key);
7608 if (prop instanceof AST_ClassField
7609 && prop.static
7610 && prop.value
7611 && !(prop.value instanceof AST_Lambda)) {
7612 if (prop.value.contains_this()) return this;
7613 values.push(prop.value);
7614 }
7615 }
7616 var base = this.extends;
7617 if (base) {
7618 if (base instanceof AST_SymbolRef) base = base.fixed_value();
7619 base = !safe_for_extends(base);
7620 if (!base) exprs.unshift(this.extends);
7621 }
7622 exprs = trim(exprs, compressor, first_in_statement);
7623 if (exprs) first_in_statement = false;
7624 values = trim(values, compressor, first_in_statement);
7625 if (!exprs) {
7626 if (!base && !values) return null;
7627 exprs = [];
7628 }
7629 if (base) {
7630 var node = to_class_expr(this, true);
7631 node.properties = [];
7632 if (exprs.length) node.properties.push(make_node(AST_ClassMethod, this, {
7633 key: make_sequence(this, exprs),
7634 value: make_node(AST_Function, this, {
7635 argnames: [],
7636 body: [],
7637 }).init_vars(node),
7638 }));
7639 exprs = [ node ];
7640 }
7641 if (values) exprs.push(make_node(AST_Call, this, {
7642 expression: make_node(AST_Arrow, this, {
7643 argnames: [],
7644 body: [],
7645 value: make_sequence(this, values),
7646 }).init_vars(this.parent_scope),
7647 args: [],
7648 }));
7649 return make_sequence(this, exprs);
7650 });
7651 def(AST_Conditional, function(compressor) {
7652 var consequent = this.consequent.drop_side_effect_free(compressor);
7653 var alternative = this.alternative.drop_side_effect_free(compressor);
7654 if (consequent === this.consequent && alternative === this.alternative) return this;
7655 var exprs;
7656 if (compressor.option("ie8")) {
7657 exprs = [];
7658 if (consequent instanceof AST_Function) {
7659 exprs.push(consequent);
7660 consequent = null;
7661 }
7662 if (alternative instanceof AST_Function) {
7663 exprs.push(alternative);
7664 alternative = null;
7665 }
7666 }
7667 var node;
7668 if (!consequent) {
7669 node = alternative ? make_node(AST_Binary, this, {
7670 operator: "||",
7671 left: this.condition,
7672 right: alternative
7673 }) : this.condition.drop_side_effect_free(compressor);
7674 } else if (!alternative) {
7675 node = make_node(AST_Binary, this, {
7676 operator: "&&",
7677 left: this.condition,
7678 right: consequent
7679 });
7680 } else {
7681 node = this.clone();
7682 node.consequent = consequent;
7683 node.alternative = alternative;
7684 }
7685 if (!compressor.option("ie8")) return node;
7686 if (node) exprs.push(node);
7687 return exprs.length == 0 ? null : make_sequence(this, exprs);
7688 });
7689 def(AST_Constant, return_null);
7690 def(AST_Dot, function(compressor, first_in_statement) {
7691 var expr = this.expression;
7692 if (!this.optional && expr.may_throw_on_access(compressor)) return this;
7693 return expr.drop_side_effect_free(compressor, first_in_statement);
7694 });
7695 def(AST_Function, function(compressor) {
7696 return fn_name_unused(this, compressor) ? null : this;
7697 });
7698 def(AST_LambdaExpression, return_null);
7699 def(AST_Object, function(compressor, first_in_statement) {
7700 var exprs = [];
7701 this.properties.forEach(function(prop) {
7702 if (prop instanceof AST_Spread) {
7703 exprs.push(prop);
7704 } else {
7705 if (prop.key instanceof AST_Node) exprs.push(prop.key);
7706 exprs.push(prop.value);
7707 }
7708 });
7709 var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
7710 var exp = node.expression;
7711 return spread_side_effects(exp) ? node : exp.drop_side_effect_free(compressor, first_in_statement);
7712 });
7713 if (!values) return null;
7714 if (values === exprs && !all(values, function(node) {
7715 return !(node instanceof AST_Spread);
7716 })) return this;
7717 return make_sequence(this, values.map(function(node) {
7718 return node instanceof AST_Spread ? make_node(AST_Object, node, {
7719 properties: [ node ],
7720 }) : node;
7721 }));
7722 });
7723 def(AST_ObjectIdentity, return_null);
7724 def(AST_Sequence, function(compressor, first_in_statement) {
7725 var expressions = trim(this.expressions, compressor, first_in_statement);
7726 if (!expressions) return null;
7727 var end = expressions.length - 1;
7728 var last = expressions[end];
7729 if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) {
7730 expressions = expressions.slice(0, -1);
7731 end--;
7732 last.expression = expressions[end];
7733 expressions[end] = last;
7734 }
7735 var assign, cond, lhs;
7736 if (compressor.option("conditionals")
7737 && end > 0
7738 && (assign = expressions[end - 1]) instanceof AST_Assign
7739 && assign.operator == "="
7740 && (lhs = assign.left) instanceof AST_SymbolRef
7741 && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) {
7742 assign = assign.clone();
7743 assign.right = cond;
7744 expressions = expressions.slice(0, -2);
7745 expressions.push(assign.drop_side_effect_free(compressor, first_in_statement));
7746 }
7747 return expressions === this.expressions ? this : make_sequence(this, expressions);
7748 });
7749 def(AST_Sub, function(compressor, first_in_statement) {
7750 var expr = this.expression;
7751 var prop = this.property;
7752 if (expr.may_throw_on_access(compressor)) {
7753 if (!this.optional) return this;
7754 prop = prop.drop_side_effect_free(compressor);
7755 if (!prop) return expr.drop_side_effect_free(compressor, first_in_statement);
7756 var node = this.clone();
7757 node.property = prop;
7758 return node;
7759 }
7760 expr = expr.drop_side_effect_free(compressor, first_in_statement);
7761 if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement);
7762 prop = prop.drop_side_effect_free(compressor);
7763 if (!prop) return expr;
7764 return make_sequence(this, [ expr, prop ]);
7765 });
7766 def(AST_SymbolRef, function(compressor) {
7767 return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
7768 });
7769 def(AST_Template, function(compressor, first_in_statement) {
7770 var self = this;
7771 if (self.is_expr_pure(compressor)) {
7772 var expressions = self.expressions;
7773 if (expressions.length == 0) return null;
7774 return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
7775 }
7776 var tag = self.tag;
7777 if (drop_returns(compressor, tag)) {
7778 // always shallow clone to signal internal changes
7779 self = self.clone();
7780 self.tag = tag.clone();
7781 // avoid extraneous traversal
7782 if (tag._squeezed) self.tag._squeezed = true;
7783 }
7784 return self;
7785 });
7786 def(AST_Unary, function(compressor, first_in_statement) {
7787 var exp = this.expression;
7788 if (unary_side_effects[this.operator]) {
7789 this.write_only = !exp.has_side_effects(compressor);
7790 return this;
7791 }
7792 if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) {
7793 return null;
7794 }
7795 var node = exp.drop_side_effect_free(compressor, first_in_statement);
7796 if (first_in_statement && node && is_iife_call(node)) {
7797 if (node === exp && this.operator == "!") return this;
7798 return node.negate(compressor, first_in_statement);
7799 }
7800 return node;
7801 });
7802 })(function(node, func) {
7803 node.DEFMETHOD("drop_side_effect_free", func);
7804 });
7805
7806 OPT(AST_SimpleStatement, function(self, compressor) {
7807 if (compressor.option("side_effects")) {
7808 var body = self.body;
7809 var node = body.drop_side_effect_free(compressor, true);
7810 if (!node) {
7811 AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
7812 return make_node(AST_EmptyStatement, self);
7813 }
7814 if (node !== body) {
7815 return make_node(AST_SimpleStatement, self, { body: node });
7816 }
7817 }
7818 return self;
7819 });
7820
7821 OPT(AST_While, function(self, compressor) {
7822 return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
7823 });
7824
7825 function has_loop_control(loop, parent, type) {
7826 if (!type) type = AST_LoopControl;
7827 var found = false;
7828 var tw = new TreeWalker(function(node) {
7829 if (found || node instanceof AST_Scope) return true;
7830 if (node instanceof type && tw.loopcontrol_target(node) === loop) {
7831 return found = true;
7832 }
7833 });
7834 if (parent instanceof AST_LabeledStatement) tw.push(parent);
7835 tw.push(loop);
7836 loop.body.walk(tw);
7837 return found;
7838 }
7839
7840 OPT(AST_Do, function(self, compressor) {
7841 if (!compressor.option("loops")) return self;
7842 var cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
7843 if (!(cond instanceof AST_Node)) {
7844 if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, {
7845 body: make_node(AST_BlockStatement, self.body, {
7846 body: [
7847 self.body,
7848 make_node(AST_SimpleStatement, self.condition, {
7849 body: self.condition
7850 }),
7851 ]
7852 })
7853 }).optimize(compressor);
7854 if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, {
7855 body: [
7856 self.body,
7857 make_node(AST_SimpleStatement, self.condition, {
7858 body: self.condition
7859 }),
7860 ]
7861 }).optimize(compressor);
7862 }
7863 if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
7864 var body = self.body.body;
7865 for (var i = body.length; --i >= 0;) {
7866 var stat = body[i];
7867 if (stat instanceof AST_If
7868 && !stat.alternative
7869 && stat.body instanceof AST_Break
7870 && compressor.loopcontrol_target(stat.body) === self) {
7871 if (has_block_scope_refs(stat.condition)) break;
7872 self.condition = make_node(AST_Binary, self, {
7873 operator: "&&",
7874 left: stat.condition.negate(compressor),
7875 right: self.condition,
7876 });
7877 body.splice(i, 1);
7878 } else if (stat instanceof AST_SimpleStatement) {
7879 if (has_block_scope_refs(stat.body)) break;
7880 self.condition = make_sequence(self, [
7881 stat.body,
7882 self.condition,
7883 ]);
7884 body.splice(i, 1);
7885 } else if (!is_declaration(stat)) {
7886 break;
7887 }
7888 }
7889 self.body = trim_block(self.body, compressor.parent());
7890 }
7891 if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
7892 if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
7893 condition: make_sequence(self.condition, [
7894 self.body.body,
7895 self.condition
7896 ]),
7897 body: make_node(AST_EmptyStatement, self)
7898 }).optimize(compressor);
7899 return self;
7900
7901 function has_block_scope_refs(node) {
7902 var found = false;
7903 node.walk(new TreeWalker(function(node) {
7904 if (found) return true;
7905 if (node instanceof AST_SymbolRef) {
7906 if (!member(node.definition(), self.enclosed)) found = true;
7907 return true;
7908 }
7909 }));
7910 return found;
7911 }
7912 });
7913
7914 function if_break_in_loop(self, compressor) {
7915 var first = first_statement(self.body);
7916 if (compressor.option("dead_code")
7917 && (first instanceof AST_Break
7918 || first instanceof AST_Continue && external_target(first)
7919 || first instanceof AST_Exit)) {
7920 var body = [];
7921 if (is_statement(self.init)) {
7922 body.push(self.init);
7923 } else if (self.init) {
7924 body.push(make_node(AST_SimpleStatement, self.init, {
7925 body: self.init
7926 }));
7927 }
7928 var retain = external_target(first) || first instanceof AST_Exit;
7929 if (self.condition && retain) {
7930 body.push(make_node(AST_If, self, {
7931 condition: self.condition,
7932 body: first,
7933 alternative: null
7934 }));
7935 } else if (self.condition) {
7936 body.push(make_node(AST_SimpleStatement, self.condition, {
7937 body: self.condition
7938 }));
7939 } else if (retain) {
7940 body.push(first);
7941 }
7942 extract_declarations_from_unreachable_code(compressor, self.body, body);
7943 return make_node(AST_BlockStatement, self, {
7944 body: body
7945 });
7946 }
7947 if (first instanceof AST_If) {
7948 var ab = first_statement(first.body);
7949 if (ab instanceof AST_Break && !external_target(ab)) {
7950 if (self.condition) {
7951 self.condition = make_node(AST_Binary, self.condition, {
7952 left: self.condition,
7953 operator: "&&",
7954 right: first.condition.negate(compressor),
7955 });
7956 } else {
7957 self.condition = first.condition.negate(compressor);
7958 }
7959 var body = as_statement_array(first.alternative);
7960 extract_declarations_from_unreachable_code(compressor, first.body, body);
7961 return drop_it(body);
7962 }
7963 ab = first_statement(first.alternative);
7964 if (ab instanceof AST_Break && !external_target(ab)) {
7965 if (self.condition) {
7966 self.condition = make_node(AST_Binary, self.condition, {
7967 left: self.condition,
7968 operator: "&&",
7969 right: first.condition,
7970 });
7971 } else {
7972 self.condition = first.condition;
7973 }
7974 var body = as_statement_array(first.body);
7975 extract_declarations_from_unreachable_code(compressor, first.alternative, body);
7976 return drop_it(body);
7977 }
7978 }
7979 return self;
7980
7981 function first_statement(body) {
7982 return body instanceof AST_BlockStatement ? body.body[0] : body;
7983 }
7984
7985 function external_target(node) {
7986 return compressor.loopcontrol_target(node) !== compressor.self();
7987 }
7988
7989 function drop_it(rest) {
7990 if (self.body instanceof AST_BlockStatement) {
7991 self.body = self.body.clone();
7992 self.body.body = rest.concat(self.body.body.slice(1));
7993 self.body = self.body.transform(compressor);
7994 } else {
7995 self.body = make_node(AST_BlockStatement, self.body, {
7996 body: rest
7997 }).transform(compressor);
7998 }
7999 return if_break_in_loop(self, compressor);
8000 }
8001 }
8002
8003 OPT(AST_For, function(self, compressor) {
8004 if (!compressor.option("loops")) return self;
8005 if (compressor.option("side_effects")) {
8006 if (self.init) self.init = self.init.drop_side_effect_free(compressor);
8007 if (self.step) self.step = self.step.drop_side_effect_free(compressor);
8008 }
8009 if (self.condition) {
8010 var cond = self.condition.evaluate(compressor);
8011 if (cond instanceof AST_Node) {
8012 cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
8013 } else if (cond) {
8014 self.condition = null;
8015 } else if (!compressor.option("dead_code")) {
8016 var orig = self.condition;
8017 self.condition = make_node_from_constant(cond, self.condition);
8018 self.condition = best_of_expression(self.condition.transform(compressor), orig);
8019 }
8020 if (!cond) {
8021 if (compressor.option("dead_code")) {
8022 var body = [];
8023 if (is_statement(self.init)) {
8024 body.push(self.init);
8025 } else if (self.init) {
8026 body.push(make_node(AST_SimpleStatement, self.init, {
8027 body: self.init
8028 }));
8029 }
8030 body.push(make_node(AST_SimpleStatement, self.condition, {
8031 body: self.condition
8032 }));
8033 extract_declarations_from_unreachable_code(compressor, self.body, body);
8034 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8035 }
8036 } else if (self.condition && !(cond instanceof AST_Node)) {
8037 self.body = make_node(AST_BlockStatement, self.body, {
8038 body: [
8039 make_node(AST_SimpleStatement, self.condition, {
8040 body: self.condition
8041 }),
8042 self.body
8043 ]
8044 });
8045 self.condition = null;
8046 }
8047 }
8048 return if_break_in_loop(self, compressor);
8049 });
8050
8051 OPT(AST_ForEnumeration, function(self, compressor) {
8052 if (compressor.option("varify") && is_lexical_definition(self.init)) {
8053 var name = self.init.definitions[0].name;
8054 if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
8055 && !name.match_symbol(function(node) {
8056 if (node instanceof AST_SymbolDeclaration) {
8057 var def = node.definition();
8058 return !same_scope(def) || may_overlap(compressor, def);
8059 }
8060 }, true)) {
8061 self.init = to_var(self.init);
8062 }
8063 }
8064 return self;
8065 });
8066
8067 function mark_locally_defined(condition, consequent, alternative) {
8068 if (!(condition instanceof AST_Binary)) return;
8069 if (!(condition.left instanceof AST_String)) {
8070 switch (condition.operator) {
8071 case "&&":
8072 mark_locally_defined(condition.left, consequent);
8073 mark_locally_defined(condition.right, consequent);
8074 break;
8075 case "||":
8076 mark_locally_defined(negate(condition.left), alternative);
8077 mark_locally_defined(negate(condition.right), alternative);
8078 break;
8079 }
8080 return;
8081 }
8082 if (!(condition.right instanceof AST_UnaryPrefix)) return;
8083 if (condition.right.operator != "typeof") return;
8084 var sym = condition.right.expression;
8085 if (!is_undeclared_ref(sym)) return;
8086 var body;
8087 var undef = condition.left.value == "undefined";
8088 switch (condition.operator) {
8089 case "==":
8090 body = undef ? alternative : consequent;
8091 break;
8092 case "!=":
8093 body = undef ? consequent : alternative;
8094 break;
8095 default:
8096 return;
8097 }
8098 if (!body) return;
8099 var def = sym.definition();
8100 var tw = new TreeWalker(function(node) {
8101 if (node instanceof AST_Scope) {
8102 var parent = tw.parent();
8103 if (parent instanceof AST_Call && parent.expression === node) return;
8104 return true;
8105 }
8106 if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
8107 });
8108 body.walk(tw);
8109
8110 function negate(node) {
8111 if (!(node instanceof AST_Binary)) return;
8112 switch (node.operator) {
8113 case "==":
8114 node = node.clone();
8115 node.operator = "!=";
8116 return node;
8117 case "!=":
8118 node = node.clone();
8119 node.operator = "==";
8120 return node;
8121 }
8122 }
8123 }
8124
8125 OPT(AST_If, function(self, compressor) {
8126 if (is_empty(self.alternative)) self.alternative = null;
8127
8128 if (!compressor.option("conditionals")) return self;
8129 // if condition can be statically determined, warn and drop
8130 // one of the blocks. note, statically determined implies
8131 // “has no side effects”; also it doesn't work for cases like
8132 // `x && true`, though it probably should.
8133 var cond = self.condition.evaluate(compressor);
8134 if (!compressor.option("dead_code") && !(cond instanceof AST_Node)) {
8135 var orig = self.condition;
8136 self.condition = make_node_from_constant(cond, orig);
8137 self.condition = best_of_expression(self.condition.transform(compressor), orig);
8138 }
8139 if (compressor.option("dead_code")) {
8140 if (cond instanceof AST_Node) {
8141 cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
8142 }
8143 if (!cond) {
8144 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
8145 var body = [
8146 make_node(AST_SimpleStatement, self.condition, {
8147 body: self.condition
8148 }),
8149 ];
8150 extract_declarations_from_unreachable_code(compressor, self.body, body);
8151 if (self.alternative) body.push(self.alternative);
8152 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8153 } else if (!(cond instanceof AST_Node)) {
8154 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
8155 var body = [
8156 make_node(AST_SimpleStatement, self.condition, {
8157 body: self.condition
8158 }),
8159 self.body,
8160 ];
8161 if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body);
8162 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8163 }
8164 }
8165 var negated = self.condition.negate(compressor);
8166 var self_condition_length = self.condition.print_to_string().length;
8167 var negated_length = negated.print_to_string().length;
8168 var negated_is_best = negated_length < self_condition_length;
8169 if (self.alternative && negated_is_best) {
8170 negated_is_best = false; // because we already do the switch here.
8171 // no need to swap values of self_condition_length and negated_length
8172 // here because they are only used in an equality comparison later on.
8173 self.condition = negated;
8174 var tmp = self.body;
8175 self.body = self.alternative || make_node(AST_EmptyStatement, self);
8176 self.alternative = tmp;
8177 }
8178 var body = [], var_defs = [], refs = [];
8179 var body_exprs = sequencesize(self.body, body, var_defs, refs);
8180 var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
8181 if (body_exprs && alt_exprs) {
8182 if (var_defs.length > 0) body.push(make_node(AST_Var, self, {
8183 definitions: var_defs
8184 }));
8185 if (body_exprs.length == 0) {
8186 body.push(make_node(AST_SimpleStatement, self.condition, {
8187 body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
8188 operator : "||",
8189 left : self.condition,
8190 right : make_sequence(self.alternative, alt_exprs)
8191 }).transform(compressor) : self.condition.clone()
8192 }).optimize(compressor));
8193 } else if (alt_exprs.length == 0) {
8194 if (self_condition_length === negated_length && !negated_is_best
8195 && self.condition instanceof AST_Binary && self.condition.operator == "||") {
8196 // although the code length of self.condition and negated are the same,
8197 // negated does not require additional surrounding parentheses.
8198 // see https://github.com/mishoo/UglifyJS/issues/979
8199 negated_is_best = true;
8200 }
8201 body.push(make_node(AST_SimpleStatement, self, {
8202 body: make_node(AST_Binary, self, {
8203 operator : negated_is_best ? "||" : "&&",
8204 left : negated_is_best ? negated : self.condition,
8205 right : make_sequence(self.body, body_exprs)
8206 }).transform(compressor)
8207 }).optimize(compressor));
8208 } else {
8209 body.push(make_node(AST_SimpleStatement, self, {
8210 body: make_node(AST_Conditional, self, {
8211 condition : self.condition,
8212 consequent : make_sequence(self.body, body_exprs),
8213 alternative : make_sequence(self.alternative, alt_exprs)
8214 })
8215 }).optimize(compressor));
8216 }
8217 refs.forEach(function(ref) {
8218 ref.definition().references.push(ref);
8219 });
8220 return make_node(AST_BlockStatement, self, {
8221 body: body
8222 }).optimize(compressor);
8223 }
8224 if (is_empty(self.body)) {
8225 self = make_node(AST_If, self, {
8226 condition: negated,
8227 body: self.alternative,
8228 alternative: null
8229 });
8230 }
8231 if (self.body instanceof AST_Exit
8232 && self.alternative instanceof AST_Exit
8233 && self.body.TYPE == self.alternative.TYPE) {
8234 var exit = make_node(self.body.CTOR, self, {
8235 value: make_node(AST_Conditional, self, {
8236 condition : self.condition,
8237 consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
8238 alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
8239 })
8240 });
8241 if (exit instanceof AST_Return) {
8242 exit.in_bool = self.body.in_bool || self.alternative.in_bool;
8243 }
8244 return exit;
8245 }
8246 if (self.body instanceof AST_If
8247 && !self.body.alternative
8248 && !self.alternative) {
8249 self = make_node(AST_If, self, {
8250 condition: make_node(AST_Binary, self.condition, {
8251 operator: "&&",
8252 left: self.condition,
8253 right: self.body.condition
8254 }),
8255 body: self.body.body,
8256 alternative: null
8257 });
8258 }
8259 if (aborts(self.body)) {
8260 if (self.alternative) {
8261 var alt = self.alternative;
8262 self.alternative = null;
8263 return make_node(AST_BlockStatement, self, {
8264 body: [ self, alt ]
8265 }).optimize(compressor);
8266 }
8267 }
8268 if (aborts(self.alternative)) {
8269 var body = self.body;
8270 self.body = self.alternative;
8271 self.condition = negated_is_best ? negated : self.condition.negate(compressor);
8272 self.alternative = null;
8273 return make_node(AST_BlockStatement, self, {
8274 body: [ self, body ]
8275 }).optimize(compressor);
8276 }
8277 if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
8278 return self;
8279
8280 function sequencesize(stat, defuns, var_defs, refs) {
8281 if (stat == null) return [];
8282 if (stat instanceof AST_BlockStatement) {
8283 var exprs = [];
8284 for (var i = 0; i < stat.body.length; i++) {
8285 var line = stat.body[i];
8286 if (line instanceof AST_LambdaDefinition) {
8287 defuns.push(line);
8288 } else if (line instanceof AST_EmptyStatement) {
8289 continue;
8290 } else if (line instanceof AST_SimpleStatement) {
8291 if (!compressor.option("sequences") && exprs.length > 0) return;
8292 exprs.push(line.body);
8293 } else if (line instanceof AST_Var) {
8294 if (!compressor.option("sequences") && exprs.length > 0) return;
8295 line.remove_initializers(compressor, var_defs);
8296 line.definitions.forEach(process_var_def);
8297 } else {
8298 return;
8299 }
8300 }
8301 return exprs;
8302 }
8303 if (stat instanceof AST_LambdaDefinition) {
8304 defuns.push(stat);
8305 return [];
8306 }
8307 if (stat instanceof AST_EmptyStatement) return [];
8308 if (stat instanceof AST_SimpleStatement) return [ stat.body ];
8309 if (stat instanceof AST_Var) {
8310 var exprs = [];
8311 stat.remove_initializers(compressor, var_defs);
8312 stat.definitions.forEach(process_var_def);
8313 return exprs;
8314 }
8315
8316 function process_var_def(var_def) {
8317 if (!var_def.value) return;
8318 exprs.push(make_node(AST_Assign, var_def, {
8319 operator: "=",
8320 left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) {
8321 refs.push(ref);
8322 }),
8323 right: var_def.value
8324 }));
8325 }
8326 }
8327 });
8328
8329 OPT(AST_Switch, function(self, compressor) {
8330 if (!compressor.option("switches")) return self;
8331 var branch;
8332 var value = self.expression.evaluate(compressor);
8333 if (!(value instanceof AST_Node)) {
8334 var orig = self.expression;
8335 self.expression = make_node_from_constant(value, orig);
8336 self.expression = best_of_expression(self.expression.transform(compressor), orig);
8337 }
8338 if (!compressor.option("dead_code")) return self;
8339 if (value instanceof AST_Node) {
8340 value = self.expression.evaluate(compressor, true);
8341 }
8342 var decl = [];
8343 var body = [];
8344 var default_branch;
8345 var exact_match;
8346 for (var i = 0, len = self.body.length; i < len && !exact_match; i++) {
8347 branch = self.body[i];
8348 if (branch instanceof AST_Default) {
8349 var prev = body[body.length - 1];
8350 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
8351 eliminate_branch(branch, prev);
8352 continue;
8353 } else {
8354 default_branch = branch;
8355 }
8356 } else if (!(value instanceof AST_Node)) {
8357 var exp = branch.expression.evaluate(compressor);
8358 if (!(exp instanceof AST_Node) && exp !== value) {
8359 eliminate_branch(branch, body[body.length - 1]);
8360 continue;
8361 }
8362 if (exp instanceof AST_Node) exp = branch.expression.evaluate(compressor, true);
8363 if (exp === value) {
8364 exact_match = branch;
8365 if (default_branch) {
8366 var default_index = body.indexOf(default_branch);
8367 body.splice(default_index, 1);
8368 eliminate_branch(default_branch, body[default_index - 1]);
8369 default_branch = null;
8370 }
8371 }
8372 }
8373 if (aborts(branch)) {
8374 var prev = body[body.length - 1];
8375 if (aborts(prev) && prev.body.length == branch.body.length
8376 && make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) {
8377 prev.body = [];
8378 }
8379 }
8380 body.push(branch);
8381 }
8382 while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
8383 while (branch = body[body.length - 1]) {
8384 var stat = branch.body[branch.body.length - 1];
8385 if (is_break(stat, compressor)) branch.body.pop();
8386 if (branch === default_branch) {
8387 if (!has_declarations_only(branch)) break;
8388 } else if (branch.expression.has_side_effects(compressor)) {
8389 break;
8390 } else if (default_branch) {
8391 if (!has_declarations_only(default_branch)) break;
8392 if (body[body.length - 2] !== default_branch) break;
8393 default_branch.body = default_branch.body.concat(branch.body);
8394 branch.body = [];
8395 } else if (!has_declarations_only(branch)) break;
8396 eliminate_branch(branch);
8397 if (body.pop() === default_branch) default_branch = null;
8398 }
8399 if (body.length == 0) {
8400 return make_node(AST_BlockStatement, self, {
8401 body: decl.concat(make_node(AST_SimpleStatement, self.expression, {
8402 body: self.expression
8403 }))
8404 }).optimize(compressor);
8405 }
8406 body[0].body = decl.concat(body[0].body);
8407 self.body = body;
8408 if (compressor.option("conditionals")) switch (body.length) {
8409 case 1:
8410 if (!no_break(body[0])) break;
8411 var exp = body[0].expression;
8412 var statements = body[0].body.slice();
8413 if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
8414 condition: make_node(AST_Binary, self, {
8415 operator: "===",
8416 left: self.expression,
8417 right: exp,
8418 }),
8419 body: make_node(AST_BlockStatement, self, {
8420 body: statements,
8421 }),
8422 alternative: null,
8423 }).optimize(compressor);
8424 if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
8425 body: exp,
8426 }));
8427 statements.unshift(make_node(AST_SimpleStatement, self.expression, {
8428 body:self.expression,
8429 }));
8430 return make_node(AST_BlockStatement, self, {
8431 body: statements,
8432 }).optimize(compressor);
8433 case 2:
8434 if (!member(default_branch, body) || !no_break(body[1])) break;
8435 var statements = body[0].body.slice();
8436 var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
8437 if (exclusive) statements.pop();
8438 if (!all(statements, no_break)) break;
8439 var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1], body[1]);
8440 var node = make_node(AST_If, self, {
8441 condition: make_node(AST_Binary, self, body[0] === default_branch ? {
8442 operator: "!==",
8443 left: self.expression,
8444 right: body[1].expression,
8445 } : {
8446 operator: "===",
8447 left: self.expression,
8448 right: body[0].expression,
8449 }),
8450 body: make_node(AST_BlockStatement, body[0], {
8451 body: statements,
8452 }),
8453 alternative: exclusive && alternative || null,
8454 });
8455 if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, {
8456 body: [ node, alternative ],
8457 });
8458 return node.optimize(compressor);
8459 }
8460 return self;
8461
8462 function is_break(node, tw) {
8463 return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
8464 }
8465
8466 function no_break(node) {
8467 var found = false;
8468 var tw = new TreeWalker(function(node) {
8469 if (found
8470 || node instanceof AST_Lambda
8471 || node instanceof AST_SimpleStatement) return true;
8472 if (is_break(node, tw)) found = true;
8473 });
8474 tw.push(self);
8475 node.walk(tw);
8476 return !found;
8477 }
8478
8479 function eliminate_branch(branch, prev) {
8480 if (prev && !aborts(prev)) {
8481 prev.body = prev.body.concat(branch.body);
8482 } else {
8483 extract_declarations_from_unreachable_code(compressor, branch, decl);
8484 }
8485 }
8486 });
8487
8488 OPT(AST_Try, function(self, compressor) {
8489 self.body = tighten_body(self.body, compressor);
8490 if (compressor.option("dead_code")) {
8491 if (has_declarations_only(self)
8492 && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
8493 return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
8494 }, true))) {
8495 var body = [];
8496 if (self.bcatch) {
8497 extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
8498 body.forEach(function(stat) {
8499 if (!(stat instanceof AST_Var)) return;
8500 stat.definitions.forEach(function(var_def) {
8501 var def = var_def.name.definition().redefined();
8502 if (!def) return;
8503 var_def.name = var_def.name.clone();
8504 var_def.name.thedef = def;
8505 });
8506 });
8507 }
8508 body.unshift(make_node(AST_BlockStatement, self, self).optimize(compressor));
8509 if (self.bfinally) {
8510 body.push(make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor));
8511 }
8512 return make_node(AST_BlockStatement, self, {
8513 body: body
8514 }).optimize(compressor);
8515 }
8516 if (self.bfinally && has_declarations_only(self.bfinally)) {
8517 var body = make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor);
8518 body = self.body.concat(body);
8519 if (!self.bcatch) return make_node(AST_BlockStatement, self, {
8520 body: body
8521 }).optimize(compressor);
8522 self.body = body;
8523 self.bfinally = null;
8524 }
8525 }
8526 return self;
8527 });
8528
8529 function remove_initializers(make_value) {
8530 return function(compressor, defns) {
8531 var dropped = false;
8532 this.definitions.forEach(function(defn) {
8533 if (defn.value) dropped = true;
8534 defn.name.match_symbol(function(node) {
8535 if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, {
8536 name: node,
8537 value: make_value(compressor, node)
8538 }));
8539 }, true);
8540 });
8541 return dropped;
8542 };
8543 }
8544
8545 AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) {
8546 return make_node(AST_Undefined, node).optimize(compressor);
8547 }));
8548 AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null));
8549 AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null));
8550
8551 AST_Definitions.DEFMETHOD("to_assignments", function() {
8552 var assignments = this.definitions.reduce(function(a, defn) {
8553 var def = defn.name.definition();
8554 var value = defn.value;
8555 if (value) {
8556 if (value instanceof AST_Sequence) value = value.clone();
8557 var name = make_node(AST_SymbolRef, defn.name, defn.name);
8558 var assign = make_node(AST_Assign, defn, {
8559 operator: "=",
8560 left: name,
8561 right: value,
8562 });
8563 a.push(assign);
8564 name.fixed = function() {
8565 return assign.right;
8566 };
8567 name.fixed.assigns = [ assign ];
8568 def.references.forEach(function(ref) {
8569 var assigns = ref.fixed && ref.fixed.assigns;
8570 if (assigns && assigns[0] === defn) assigns[0] = assign;
8571 });
8572 def.references.push(name);
8573 }
8574 def.eliminated++;
8575 def.single_use = false;
8576 return a;
8577 }, []);
8578 if (assignments.length == 0) return null;
8579 return make_sequence(this, assignments);
8580 });
8581
8582 function is_safe_lexical(def) {
8583 return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
8584 }
8585
8586 function may_overlap(compressor, def) {
8587 if (compressor.exposed(def)) return true;
8588 var scope = def.scope.resolve();
8589 for (var s = def.scope; s !== scope;) {
8590 s = s.parent_scope;
8591 if (s.var_names()[def.name]) return true;
8592 }
8593 }
8594
8595 function to_var(stat) {
8596 return make_node(AST_Var, stat, {
8597 definitions: stat.definitions.map(function(defn) {
8598 return make_node(AST_VarDef, defn, {
8599 name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
8600 var def = name.definition();
8601 def.orig[def.orig.indexOf(node)] = name;
8602 var scope = def.scope.resolve();
8603 if (def.scope === scope) return;
8604 def.scope = scope;
8605 scope.variables.set(def.name, def);
8606 scope.enclosed.push(def);
8607 scope.var_names()[def.name] = true;
8608 }),
8609 value: defn.value,
8610 });
8611 })
8612 });
8613 }
8614
8615 function can_varify(compressor, sym) {
8616 if (!sym.fixed_value()) return false;
8617 var def = sym.definition();
8618 return is_safe_lexical(def) && same_scope(def) && !may_overlap(compressor, def);
8619 }
8620
8621 function varify(self, compressor) {
8622 return compressor.option("varify") && all(self.definitions, function(defn) {
8623 return !defn.name.match_symbol(function(node) {
8624 if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
8625 }, true);
8626 }) ? to_var(self) : self;
8627 }
8628
8629 OPT(AST_Const, varify);
8630 OPT(AST_Let, varify);
8631
8632 function trim_optional_chain(self, compressor) {
8633 if (!compressor.option("optional_chains")) return;
8634 if (!self.optional) return;
8635 var expr = self.expression;
8636 var ev = expr.evaluate(compressor, true);
8637 if (ev == null) return make_node(AST_UnaryPrefix, self, {
8638 operator: "void",
8639 expression: expr,
8640 }).optimize(compressor);
8641 if (!(ev instanceof AST_Node)) self.optional = false;
8642 }
8643
8644 function lift_sequence_in_expression(node, compressor) {
8645 var exp = node.expression;
8646 if (!(exp instanceof AST_Sequence)) return node;
8647 var x = exp.expressions.slice();
8648 var e = node.clone();
8649 e.expression = x.pop();
8650 x.push(e);
8651 return make_sequence(node, x);
8652 }
8653
8654 function drop_unused_call_args(call, compressor, fns_with_marked_args) {
8655 var exp = call.expression;
8656 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
8657 if (!(fn instanceof AST_Lambda)) return;
8658 if (fn.uses_arguments) return;
8659 if (fn.pinned()) return;
8660 if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
8661 var args = call.args;
8662 if (!all(args, function(arg) {
8663 return !(arg instanceof AST_Spread);
8664 })) return;
8665 var argnames = fn.argnames;
8666 var is_iife = fn === exp && !fn.name;
8667 if (fn.rest) {
8668 if (!(is_iife && compressor.option("rests"))) return;
8669 var insert = argnames.length;
8670 args = args.slice(0, insert);
8671 while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor));
8672 args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) }));
8673 argnames = argnames.concat(fn.rest);
8674 fn.rest = null;
8675 } else {
8676 args = args.slice();
8677 argnames = argnames.slice();
8678 }
8679 var pos = 0, last = 0;
8680 var drop_defaults = is_iife && compressor.option("default_values");
8681 var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
8682 if (!argname) return true;
8683 if (argname instanceof AST_DestructuredArray) {
8684 return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
8685 }
8686 if (argname instanceof AST_DestructuredObject) {
8687 return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
8688 }
8689 return argname.__unused;
8690 } : return_false;
8691 var side_effects = [];
8692 for (var i = 0; i < args.length; i++) {
8693 var argname = argnames[i];
8694 if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
8695 argnames[i] = argname = argname.name;
8696 }
8697 if (!argname || "__unused" in argname) {
8698 var node = args[i].drop_side_effect_free(compressor);
8699 if (drop_fargs(argname)) {
8700 if (argname) argnames.splice(i, 1);
8701 args.splice(i, 1);
8702 if (node) side_effects.push(node);
8703 i--;
8704 continue;
8705 } else if (node) {
8706 side_effects.push(node);
8707 args[pos++] = make_sequence(call, side_effects);
8708 side_effects = [];
8709 } else if (argname) {
8710 if (side_effects.length) {
8711 args[pos++] = make_sequence(call, side_effects);
8712 side_effects = [];
8713 } else {
8714 args[pos++] = make_node(AST_Number, args[i], {
8715 value: 0
8716 });
8717 continue;
8718 }
8719 }
8720 } else if (drop_fargs(argname, args[i])) {
8721 var node = args[i].drop_side_effect_free(compressor);
8722 argnames.splice(i, 1);
8723 args.splice(i, 1);
8724 if (node) side_effects.push(node);
8725 i--;
8726 continue;
8727 } else {
8728 side_effects.push(args[i]);
8729 args[pos++] = make_sequence(call, side_effects);
8730 side_effects = [];
8731 }
8732 last = pos;
8733 }
8734 for (; i < argnames.length; i++) {
8735 if (drop_fargs(argnames[i])) argnames.splice(i--, 1);
8736 }
8737 fn.argnames = argnames;
8738 args.length = last;
8739 call.args = args;
8740 if (!side_effects.length) return;
8741 var arg = make_sequence(call, side_effects);
8742 args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, {
8743 operator: "void",
8744 expression: arg,
8745 }) : arg);
8746 }
8747
8748 OPT(AST_Call, function(self, compressor) {
8749 var exp = self.expression;
8750 var terminated = trim_optional_chain(self, compressor);
8751 if (terminated) return terminated;
8752 if (compressor.option("sequences")) {
8753 if (exp instanceof AST_PropAccess) {
8754 var seq = lift_sequence_in_expression(exp, compressor);
8755 if (seq !== exp) {
8756 var call = self.clone();
8757 call.expression = seq.expressions.pop();
8758 seq.expressions.push(call);
8759 return seq.optimize(compressor);
8760 }
8761 } else if (!needs_unbinding(compressor, exp.tail_node())) {
8762 var seq = lift_sequence_in_expression(self, compressor);
8763 if (seq !== self) return seq.optimize(compressor);
8764 }
8765 }
8766 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
8767 if (compressor.option("unsafe")) {
8768 if (is_undeclared_ref(exp)) switch (exp.name) {
8769 case "Array":
8770 // Array(n) ---> [ , , ... , ]
8771 if (self.args.length == 1) {
8772 var first = self.args[0];
8773 if (first instanceof AST_Number) try {
8774 var length = first.value;
8775 if (length > 6) break;
8776 var elements = Array(length);
8777 for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
8778 return make_node(AST_Array, self, { elements: elements });
8779 } catch (ex) {
8780 AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
8781 length: length,
8782 file: self.start.file,
8783 line: self.start.line,
8784 col: self.start.col,
8785 });
8786 break;
8787 }
8788 if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
8789 }
8790 // Array(...) ---> [ ... ]
8791 return make_node(AST_Array, self, { elements: self.args });
8792 case "Object":
8793 // Object() ---> {}
8794 if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
8795 break;
8796 case "String":
8797 // String() ---> ""
8798 if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
8799 // String(x) ---> "" + x
8800 if (self.args.length == 1) return make_node(AST_Binary, self, {
8801 operator: "+",
8802 left: make_node(AST_String, self, { value: "" }),
8803 right: self.args[0],
8804 }).optimize(compressor);
8805 break;
8806 case "Number":
8807 // Number() ---> 0
8808 if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
8809 // Number(x) ---> +("" + x)
8810 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
8811 operator: "+",
8812 expression: make_node(AST_Binary, self, {
8813 operator: "+",
8814 left: make_node(AST_String, self, { value: "" }),
8815 right: self.args[0],
8816 }),
8817 }).optimize(compressor);
8818 break;
8819 case "Boolean":
8820 // Boolean() ---> false
8821 if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
8822 // Boolean(x) ---> !!x
8823 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
8824 operator: "!",
8825 expression: make_node(AST_UnaryPrefix, self, {
8826 operator: "!",
8827 expression: self.args[0],
8828 }),
8829 }).optimize(compressor);
8830 break;
8831 case "RegExp":
8832 // attempt to convert RegExp(...) to literal
8833 var params = [];
8834 if (all(self.args, function(arg) {
8835 var value = arg.evaluate(compressor);
8836 params.unshift(value);
8837 return arg !== value;
8838 })) try {
8839 return best_of(compressor, self, make_node(AST_RegExp, self, {
8840 value: RegExp.apply(RegExp, params),
8841 }));
8842 } catch (ex) {
8843 AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
8844 expr: self,
8845 file: self.start.file,
8846 line: self.start.line,
8847 col: self.start.col,
8848 });
8849 }
8850 break;
8851 } else if (exp instanceof AST_Dot) switch (exp.property) {
8852 case "toString":
8853 // x.toString() ---> "" + x
8854 var expr = exp.expression;
8855 if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
8856 return make_node(AST_Binary, self, {
8857 operator: "+",
8858 left: make_node(AST_String, self, { value: "" }),
8859 right: expr,
8860 }).optimize(compressor);
8861 }
8862 break;
8863 case "join":
8864 if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
8865 var separator = self.args[0];
8866 // [].join() ---> ""
8867 // [].join(x) ---> (x, "")
8868 if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
8869 return separator ? make_sequence(self, [
8870 separator,
8871 make_node(AST_String, self, { value: "" }),
8872 ]).optimize(compressor) : make_node(AST_String, self, { value: "" });
8873 }
8874 if (separator) {
8875 separator = separator.evaluate(compressor);
8876 if (separator instanceof AST_Node) break EXIT; // not a constant
8877 }
8878 var elements = [];
8879 var consts = [];
8880 for (var i = 0; i < exp.expression.elements.length; i++) {
8881 var el = exp.expression.elements[i];
8882 var value = el.evaluate(compressor);
8883 if (value !== el) {
8884 consts.push(value);
8885 } else if (el instanceof AST_Spread) {
8886 break EXIT;
8887 } else {
8888 if (consts.length > 0) {
8889 elements.push(make_node(AST_String, self, {
8890 value: consts.join(separator),
8891 }));
8892 consts.length = 0;
8893 }
8894 elements.push(el);
8895 }
8896 }
8897 if (consts.length > 0) elements.push(make_node(AST_String, self, {
8898 value: consts.join(separator),
8899 }));
8900 // [ x ].join() ---> "" + x
8901 // [ x ].join(".") ---> "" + x
8902 // [ 1, 2, 3 ].join() ---> "1,2,3"
8903 // [ 1, 2, 3 ].join(".") ---> "1.2.3"
8904 if (elements.length == 1) {
8905 if (elements[0].is_string(compressor)) return elements[0];
8906 return make_node(AST_Binary, elements[0], {
8907 operator: "+",
8908 left: make_node(AST_String, self, { value: "" }),
8909 right: elements[0],
8910 });
8911 }
8912 // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
8913 if (separator == "") {
8914 var first;
8915 if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
8916 first = elements.shift();
8917 } else {
8918 first = make_node(AST_String, self, { value: "" });
8919 }
8920 return elements.reduce(function(prev, el) {
8921 return make_node(AST_Binary, el, {
8922 operator: "+",
8923 left: prev,
8924 right: el,
8925 });
8926 }, first).optimize(compressor);
8927 }
8928 // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
8929 // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
8930 // need this awkward cloning to not affect original element
8931 // best_of will decide which one to get through.
8932 var node = self.clone();
8933 node.expression = node.expression.clone();
8934 node.expression.expression = node.expression.expression.clone();
8935 node.expression.expression.elements = elements;
8936 return best_of(compressor, self, node);
8937 }
8938 break;
8939 case "charAt":
8940 if (self.args.length < 2) {
8941 var node = make_node(AST_Binary, self, {
8942 operator: "||",
8943 left: make_node(AST_Sub, self, {
8944 expression: exp.expression,
8945 property: self.args.length ? make_node(AST_Binary, self.args[0], {
8946 operator: "|",
8947 left: make_node(AST_Number, self, { value: 0 }),
8948 right: self.args[0],
8949 }) : make_node(AST_Number, self, { value: 0 }),
8950 }).optimize(compressor),
8951 right: make_node(AST_String, self, { value: "" }),
8952 });
8953 node.is_string = return_true;
8954 return node.optimize(compressor);
8955 }
8956 break;
8957 case "apply":
8958 if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
8959 var args = self.args[1].elements.slice();
8960 args.unshift(self.args[0]);
8961 return make_node(AST_Call, self, {
8962 expression: make_node(AST_Dot, exp, {
8963 expression: exp.expression,
8964 property: "call",
8965 }),
8966 args: args
8967 }).optimize(compressor);
8968 }
8969 break;
8970 case "call":
8971 var func = exp.expression;
8972 if (func instanceof AST_SymbolRef) {
8973 func = func.fixed_value();
8974 }
8975 if (func instanceof AST_Lambda && !func.contains_this()) {
8976 return (self.args.length ? make_sequence(this, [
8977 self.args[0],
8978 make_node(AST_Call, self, {
8979 expression: exp.expression,
8980 args: self.args.slice(1)
8981 })
8982 ]) : make_node(AST_Call, self, {
8983 expression: exp.expression,
8984 args: []
8985 })).optimize(compressor);
8986 }
8987 break;
8988 }
8989 }
8990 if (compressor.option("unsafe_Function")
8991 && is_undeclared_ref(exp)
8992 && exp.name == "Function") {
8993 // new Function() ---> function(){}
8994 if (self.args.length == 0) return make_node(AST_Function, self, {
8995 argnames: [],
8996 body: []
8997 }).init_vars(exp.scope);
8998 if (all(self.args, function(x) {
8999 return x instanceof AST_String;
9000 })) {
9001 // quite a corner-case, but we can handle it:
9002 // https://github.com/mishoo/UglifyJS/issues/203
9003 // if the code argument is a constant, then we can minify it.
9004 try {
9005 var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
9006 return arg.value;
9007 }).join() + "){" + self.args[self.args.length - 1].value + "})";
9008 var ast = parse(code);
9009 var mangle = { ie8: compressor.option("ie8") };
9010 ast.figure_out_scope(mangle);
9011 var comp = new Compressor(compressor.options);
9012 ast = ast.transform(comp);
9013 ast.figure_out_scope(mangle);
9014 ast.compute_char_frequency(mangle);
9015 ast.mangle_names(mangle);
9016 var fun;
9017 ast.walk(new TreeWalker(function(node) {
9018 if (fun) return true;
9019 if (node instanceof AST_Lambda) {
9020 fun = node;
9021 return true;
9022 }
9023 }));
9024 var code = OutputStream();
9025 AST_BlockStatement.prototype._codegen.call(fun, code);
9026 self.args = [
9027 make_node(AST_String, self, {
9028 value: fun.argnames.map(function(arg) {
9029 return arg.print_to_string();
9030 }).join(),
9031 }),
9032 make_node(AST_String, self.args[self.args.length - 1], {
9033 value: code.get().replace(/^\{|\}$/g, "")
9034 })
9035 ];
9036 return self;
9037 } catch (ex) {
9038 if (ex instanceof JS_Parse_Error) {
9039 AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
9040 AST_Node.warn(ex.toString());
9041 } else {
9042 throw ex;
9043 }
9044 }
9045 }
9046 }
9047 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9048 var parent = compressor.parent(), current = compressor.self();
9049 var is_func = fn instanceof AST_Lambda
9050 && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
9051 && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
9052 var stat = is_func && fn.first_statement();
9053 var has_default = 0, has_destructured = false;
9054 var has_spread = !all(self.args, function(arg) {
9055 return !(arg instanceof AST_Spread);
9056 });
9057 var can_drop = is_func && all(fn.argnames, function(argname, index) {
9058 if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
9059 if (argname instanceof AST_DefaultValue) {
9060 if (!has_default) has_default = 1;
9061 var arg = has_default == 1 && self.args[index];
9062 if (arg && !is_undefined(arg)) has_default = 2;
9063 if (has_arg_refs(argname.value)) return false;
9064 argname = argname.name;
9065 }
9066 if (argname instanceof AST_Destructured) {
9067 has_destructured = true;
9068 if (has_arg_refs(argname)) return false;
9069 }
9070 return true;
9071 }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn.rest));
9072 var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
9073 if (can_inline && stat instanceof AST_Return) {
9074 var value = stat.value;
9075 if (exp === fn && !fn.name && (!value || value.is_constant_expression()) && safe_from_await_yield(fn)) {
9076 return make_sequence(self, convert_args(value)).optimize(compressor);
9077 }
9078 }
9079 if (is_func) {
9080 var def, value, var_assigned = false;
9081 if (can_inline
9082 && !fn.uses_arguments
9083 && !fn.pinned()
9084 && !(fn.name && fn instanceof AST_LambdaExpression)
9085 && (exp === fn || !recursive_ref(compressor, def = exp.definition())
9086 && fn.is_constant_expression(find_scope(compressor)))
9087 && !has_spread
9088 && (value = can_flatten_body(stat))
9089 && !fn.contains_this()) {
9090 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
9091 if (can_substitute_directly()) {
9092 var args = self.args.slice();
9093 var refs = [];
9094 args.push(value.clone(true).transform(new TreeTransformer(function(node) {
9095 if (node instanceof AST_SymbolRef) {
9096 var def = node.definition();
9097 if (fn.variables.get(node.name) !== def) {
9098 refs.push(node);
9099 return node;
9100 }
9101 var index = resolve_index(def);
9102 var arg = args[index];
9103 if (!arg) return make_node(AST_Undefined, self);
9104 args[index] = null;
9105 var parent = this.parent();
9106 return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
9107 }
9108 })));
9109 var save_inlined = fn.inlined;
9110 if (exp !== fn) fn.inlined = true;
9111 var node = make_sequence(self, args.filter(function(arg) {
9112 return arg;
9113 })).optimize(compressor);
9114 fn.inlined = save_inlined;
9115 node = maintain_this_binding(compressor, parent, current, node);
9116 if (replacing || best_of_expression(node, self) === node) {
9117 refs.forEach(function(ref) {
9118 ref.scope = exp === fn ? fn.parent_scope : exp.scope;
9119 ref.reference();
9120 if (replacing) {
9121 ref.definition().replaced++;
9122 } else {
9123 ref.definition().single_use = false;
9124 }
9125 });
9126 return node;
9127 } else if (!node.has_side_effects(compressor)) {
9128 self.drop_side_effect_free = return_null;
9129 }
9130 }
9131 var arg_used, insert, in_loop, scope;
9132 if (replacing && can_inject_symbols()) {
9133 fn._squeezed = true;
9134 if (exp !== fn) fn.parent_scope = exp.scope;
9135 var node = make_sequence(self, flatten_fn()).optimize(compressor);
9136 return maintain_this_binding(compressor, parent, current, node);
9137 }
9138 }
9139 if (compressor.option("side_effects")
9140 && can_drop
9141 && all(fn.body, is_empty)
9142 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
9143 && !(is_arrow(fn) && fn.value)
9144 && safe_from_await_yield(fn)) {
9145 return make_sequence(self, convert_args()).optimize(compressor);
9146 }
9147 }
9148 if (compressor.option("drop_console")) {
9149 if (exp instanceof AST_PropAccess) {
9150 var name = exp.expression;
9151 while (name.expression) {
9152 name = name.expression;
9153 }
9154 if (is_undeclared_ref(name) && name.name == "console") {
9155 return make_node(AST_Undefined, self).optimize(compressor);
9156 }
9157 }
9158 }
9159 if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
9160 return self.negate(compressor, true);
9161 }
9162 return try_evaluate(compressor, self);
9163
9164 function has_arg_refs(node) {
9165 var found = false;
9166 node.walk(new TreeWalker(function(node) {
9167 if (found) return true;
9168 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
9169 return found = true;
9170 }
9171 }));
9172 return found;
9173 }
9174
9175 function make_void_lhs(orig) {
9176 return make_node(AST_Dot, orig, {
9177 expression: make_node(AST_Array, orig, { elements: [] }),
9178 property: "e",
9179 });
9180 }
9181
9182 function convert_args(value) {
9183 var args = self.args.slice();
9184 var destructured = has_default > 1 || has_destructured || fn.rest;
9185 if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
9186 if (destructured) {
9187 var tt = new TreeTransformer(function(node, descend) {
9188 if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
9189 name: node.name.transform(tt) || make_void_lhs(node),
9190 value: node.value,
9191 });
9192 if (node instanceof AST_DestructuredArray) {
9193 var elements = [];
9194 node.elements.forEach(function(node, index) {
9195 node = node.transform(tt);
9196 if (node) elements[index] = node;
9197 });
9198 fill_holes(node, elements);
9199 return make_node(AST_DestructuredArray, node, { elements: elements });
9200 }
9201 if (node instanceof AST_DestructuredObject) {
9202 var properties = [], side_effects = [];
9203 node.properties.forEach(function(prop) {
9204 var key = prop.key;
9205 var value = prop.value.transform(tt);
9206 if (value) {
9207 side_effects.push(key instanceof AST_Node ? key : make_node_from_constant(key, prop));
9208 properties.push(make_node(AST_DestructuredKeyVal, prop, {
9209 key: make_sequence(node, side_effects),
9210 value: value,
9211 }));
9212 side_effects = [];
9213 } else if (key instanceof AST_Node) {
9214 side_effects.push(key);
9215 }
9216 });
9217 if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
9218 key: make_sequence(node, side_effects),
9219 value: make_void_lhs(node),
9220 }));
9221 return make_node(AST_DestructuredObject, node, { properties: properties });
9222 }
9223 if (node instanceof AST_SymbolFunarg) return null;
9224 });
9225 var lhs = [];
9226 fn.argnames.forEach(function(argname, index) {
9227 argname = argname.transform(tt);
9228 if (argname) lhs[index] = argname;
9229 });
9230 var rest = fn.rest && fn.rest.transform(tt);
9231 if (rest) lhs.length = fn.argnames.length;
9232 fill_holes(fn, lhs);
9233 args[0] = make_node(AST_Assign, self, {
9234 operator: "=",
9235 left: make_node(AST_DestructuredArray, fn, {
9236 elements: lhs,
9237 rest: rest,
9238 }),
9239 right: args[0],
9240 });
9241 } else fn.argnames.forEach(function(argname) {
9242 if (argname instanceof AST_DefaultValue) args.push(argname.value);
9243 });
9244 args.push(value || make_node(AST_Undefined, self));
9245 return args;
9246 }
9247
9248 function avoid_await_yield() {
9249 var avoid = [];
9250 var parent_scope = scope || compressor.find_parent(AST_Scope);
9251 if (is_async(parent_scope)) avoid.push("await");
9252 if (is_generator(parent_scope)) avoid.push("yield");
9253 return avoid.length && makePredicate(avoid);
9254 }
9255
9256 function safe_from_await_yield(node) {
9257 var avoid = avoid_await_yield();
9258 if (!avoid) return true;
9259 var safe = true;
9260 var tw = new TreeWalker(function(node) {
9261 if (!safe) return true;
9262 if (node instanceof AST_Scope) {
9263 if (node === fn) return;
9264 if (is_arrow(node)) {
9265 for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
9266 } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
9267 safe = false;
9268 }
9269 return true;
9270 }
9271 if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
9272 });
9273 node.walk(tw);
9274 return safe;
9275 }
9276
9277 function return_value(stat) {
9278 if (!stat) return make_node(AST_Undefined, self);
9279 if (stat instanceof AST_Return) return stat.value || make_node(AST_Undefined, self);
9280 if (stat instanceof AST_SimpleStatement) return make_node(AST_UnaryPrefix, stat, {
9281 operator: "void",
9282 expression: stat.body
9283 });
9284 }
9285
9286 function can_flatten_body(stat) {
9287 var len = fn.body.length;
9288 if (len < 2) {
9289 stat = return_value(stat);
9290 if (stat) return stat;
9291 }
9292 if (compressor.option("inline") < 3) return false;
9293 stat = null;
9294 for (var i = 0; i < len; i++) {
9295 var line = fn.body[i];
9296 if (line instanceof AST_Var) {
9297 var assigned = var_assigned || !all(line.definitions, function(var_def) {
9298 return !var_def.value;
9299 });
9300 if (assigned) {
9301 var_assigned = true;
9302 if (stat) return false;
9303 }
9304 } else if (line instanceof AST_AsyncDefun
9305 || line instanceof AST_Defun
9306 || line instanceof AST_EmptyStatement) {
9307 continue;
9308 } else if (stat) {
9309 return false;
9310 } else {
9311 stat = line;
9312 }
9313 }
9314 return return_value(stat);
9315 }
9316
9317 function resolve_index(def) {
9318 for (var i = fn.argnames.length; --i >= 0;) {
9319 if (fn.argnames[i].definition() === def) return i;
9320 }
9321 }
9322
9323 function can_substitute_directly() {
9324 if (has_default || has_destructured || var_assigned || fn.rest) return;
9325 if (compressor.option("inline") < 2 && fn.argnames.length) return;
9326 if (!fn.variables.all(function(def) {
9327 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
9328 })) return;
9329 var abort = false;
9330 var avoid = avoid_await_yield();
9331 var begin;
9332 var in_order = [];
9333 var side_effects = false;
9334 value.walk(new TreeWalker(function(node, descend) {
9335 if (abort) return true;
9336 if (node instanceof AST_Binary && lazy_op[node.operator]
9337 || node instanceof AST_Conditional) {
9338 in_order = null;
9339 return;
9340 }
9341 if (node instanceof AST_Scope) return abort = true;
9342 if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
9343 if (node instanceof AST_SymbolRef) {
9344 var def = node.definition();
9345 if (fn.variables.get(node.name) !== def) {
9346 in_order = null;
9347 return;
9348 }
9349 if (def.init instanceof AST_LambdaDefinition) return abort = true;
9350 if (is_lhs(node, this.parent())) return abort = true;
9351 var index = resolve_index(def);
9352 if (!(begin < index)) begin = index;
9353 if (!in_order) return;
9354 if (side_effects) {
9355 in_order = null;
9356 } else {
9357 in_order.push(fn.argnames[index]);
9358 }
9359 return;
9360 }
9361 if (node.has_side_effects(compressor)) {
9362 descend();
9363 side_effects = true;
9364 return true;
9365 }
9366 }));
9367 if (abort) return;
9368 var end = self.args.length;
9369 if (in_order && fn.argnames.length >= end) {
9370 end = fn.argnames.length;
9371 while (end-- > begin && fn.argnames[end] === in_order.pop());
9372 end++;
9373 }
9374 var scope = side_effects && !in_order && compressor.find_parent(AST_Scope);
9375 return end <= begin || all(self.args.slice(begin, end), scope ? function(funarg) {
9376 return funarg.is_constant_expression(scope);
9377 } : function(funarg) {
9378 return !funarg.has_side_effects(compressor);
9379 });
9380 }
9381
9382 function var_exists(defined, name) {
9383 return defined[name] || identifier_atom[name] || scope.var_names()[name];
9384 }
9385
9386 function can_inject_args(defined, used, safe_to_inject) {
9387 var abort = false;
9388 fn.each_argname(function(arg) {
9389 if (abort) return;
9390 if (arg.__unused) return;
9391 if (arg instanceof AST_DefaultValue) arg = arg.name;
9392 if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
9393 used[arg.name] = true;
9394 if (in_loop) in_loop.push(arg.definition());
9395 });
9396 return !abort;
9397 }
9398
9399 function can_inject_vars(defined, used, safe_to_inject) {
9400 for (var i = 0; i < fn.body.length; i++) {
9401 var stat = fn.body[i];
9402 if (stat instanceof AST_LambdaDefinition) {
9403 if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
9404 if (!all(stat.enclosed, function(def) {
9405 return def.scope === stat || !defined[def.name];
9406 })) return false;
9407 if (in_loop) in_loop.push(stat.name.definition());
9408 continue;
9409 }
9410 if (!(stat instanceof AST_Var)) continue;
9411 if (!safe_to_inject) return false;
9412 for (var j = stat.definitions.length; --j >= 0;) {
9413 var name = stat.definitions[j].name;
9414 if (var_exists(defined, name.name)) return false;
9415 if (in_loop) in_loop.push(name.definition());
9416 }
9417 }
9418 return true;
9419 }
9420
9421 function can_inject_symbols() {
9422 var defined = Object.create(null);
9423 var level = 0, child;
9424 scope = current;
9425 do {
9426 if (scope.variables) scope.variables.each(function(def) {
9427 defined[def.name] = true;
9428 });
9429 child = scope;
9430 scope = compressor.parent(level++);
9431 if (scope instanceof AST_DWLoop) {
9432 in_loop = [];
9433 } else if (scope instanceof AST_For) {
9434 if (scope.init === child) continue;
9435 in_loop = [];
9436 } else if (scope instanceof AST_ForEnumeration) {
9437 if (scope.init === child) continue;
9438 if (scope.object === child) continue;
9439 in_loop = [];
9440 } else if (scope instanceof AST_SymbolRef) {
9441 if (scope.fixed_value() instanceof AST_Scope) return false;
9442 }
9443 } while (!(scope instanceof AST_Scope));
9444 insert = scope.body.indexOf(child) + 1;
9445 if (!insert) return false;
9446 if (!safe_from_await_yield(fn)) return false;
9447 var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
9448 if (scope instanceof AST_Toplevel) {
9449 if (compressor.toplevel.vars) {
9450 defined["arguments"] = true;
9451 } else {
9452 safe_to_inject = false;
9453 }
9454 }
9455 var inline = compressor.option("inline");
9456 arg_used = Object.create(defined);
9457 if (!can_inject_args(defined, arg_used, inline >= 2 && safe_to_inject)) return false;
9458 var used = Object.create(arg_used);
9459 if (!can_inject_vars(defined, used, inline >= 3 && safe_to_inject)) return false;
9460 return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
9461 }
9462
9463 function append_var(decls, expressions, name, value) {
9464 var def = name.definition();
9465 scope.variables.set(name.name, def);
9466 scope.enclosed.push(def);
9467 if (!scope.var_names()[name.name]) {
9468 scope.var_names()[name.name] = true;
9469 decls.push(make_node(AST_VarDef, name, {
9470 name: name,
9471 value: null
9472 }));
9473 }
9474 if (!value) return;
9475 var sym = make_node(AST_SymbolRef, name, name);
9476 def.references.push(sym);
9477 expressions.push(make_node(AST_Assign, self, {
9478 operator: "=",
9479 left: sym,
9480 right: value
9481 }));
9482 }
9483
9484 function flatten_args(decls, expressions) {
9485 var len = fn.argnames.length;
9486 for (var i = self.args.length; --i >= len;) {
9487 expressions.push(self.args[i]);
9488 }
9489 var default_args = [];
9490 for (i = len; --i >= 0;) {
9491 var argname = fn.argnames[i];
9492 var name;
9493 if (argname instanceof AST_DefaultValue) {
9494 default_args.push(argname);
9495 name = argname.name;
9496 } else {
9497 name = argname;
9498 }
9499 var value = self.args[i];
9500 if (name.__unused || scope.var_names()[name.name]) {
9501 if (value) expressions.push(value);
9502 } else {
9503 var symbol = make_node(AST_SymbolVar, name, name);
9504 name.definition().orig.push(symbol);
9505 if ("__unused" in name) {
9506 append_var(decls, expressions, symbol);
9507 if (value) expressions.push(value);
9508 } else {
9509 if (!value && in_loop && argname === name) value = make_node(AST_Undefined, self);
9510 append_var(decls, expressions, symbol, value);
9511 }
9512 }
9513 }
9514 decls.reverse();
9515 expressions.reverse();
9516 for (i = default_args.length; --i >= 0;) {
9517 var node = default_args[i];
9518 if ("__unused" in node.name) {
9519 expressions.push(node.value);
9520 } else {
9521 var sym = make_node(AST_SymbolRef, node.name, node.name);
9522 node.name.definition().references.push(sym);
9523 expressions.push(make_node(AST_Assign, node, {
9524 operator: "=",
9525 left: sym,
9526 right: node.value,
9527 }));
9528 }
9529 }
9530 }
9531
9532 function flatten_destructured(decls, expressions) {
9533 expressions.push(make_node(AST_Assign, self, {
9534 operator: "=",
9535 left: make_node(AST_DestructuredArray, self, {
9536 elements: fn.argnames.map(function(argname) {
9537 return argname.convert_symbol(AST_SymbolRef, process);
9538 }),
9539 rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
9540 }),
9541 right: make_node(AST_Array, self, { elements: self.args.slice() }),
9542 }));
9543
9544 function process(ref, name) {
9545 var symbol = make_node(AST_SymbolVar, name, name);
9546 name.definition().orig.push(symbol);
9547 append_var(decls, expressions, symbol);
9548 }
9549 }
9550
9551 function flatten_var(name) {
9552 var redef = name.definition().redefined();
9553 if (redef) {
9554 name = name.clone();
9555 name.thedef = redef;
9556 }
9557 return name;
9558 }
9559
9560 function flatten_vars(decls, expressions) {
9561 var args = [ insert, 0 ];
9562 var decl_var = [], expr_var = [], expr_loop = [];
9563 for (var i = 0; i < fn.body.length; i++) {
9564 var stat = fn.body[i];
9565 if (stat instanceof AST_LambdaDefinition) {
9566 if (in_loop) {
9567 var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
9568 name.definition().orig.push(name);
9569 append_var(decls, expressions, name, to_func_expr(stat, true));
9570 } else {
9571 var def = stat.name.definition();
9572 scope.functions.set(def.name, def);
9573 scope.variables.set(def.name, def);
9574 scope.enclosed.push(def);
9575 scope.var_names()[def.name] = true;
9576 args.push(stat);
9577 }
9578 continue;
9579 }
9580 if (!(stat instanceof AST_Var)) continue;
9581 for (var j = 0; j < stat.definitions.length; j++) {
9582 var var_def = stat.definitions[j];
9583 var name = flatten_var(var_def.name);
9584 append_var(decl_var, expr_var, name, var_def.value);
9585 if (in_loop && !HOP(arg_used, name.name)) {
9586 var def = fn.variables.get(name.name);
9587 var sym = make_node(AST_SymbolRef, name, name);
9588 def.references.push(sym);
9589 expr_loop.push(make_node(AST_Assign, var_def, {
9590 operator: "=",
9591 left: sym,
9592 right: make_node(AST_Undefined, name),
9593 }));
9594 }
9595 }
9596 }
9597 [].push.apply(decls, decl_var);
9598 [].push.apply(expressions, expr_loop);
9599 [].push.apply(expressions, expr_var);
9600 return args;
9601 }
9602
9603 function flatten_fn() {
9604 var decls = [];
9605 var expressions = [];
9606 if (has_default > 1 || has_destructured || fn.rest) {
9607 flatten_destructured(decls, expressions);
9608 } else {
9609 flatten_args(decls, expressions);
9610 }
9611 var args = flatten_vars(decls, expressions);
9612 expressions.push(value);
9613 if (decls.length) args.push(make_node(AST_Var, fn, {
9614 definitions: decls
9615 }));
9616 [].splice.apply(scope.body, args);
9617 fn.enclosed.forEach(function(def) {
9618 if (scope.var_names()[def.name]) return;
9619 scope.enclosed.push(def);
9620 scope.var_names()[def.name] = true;
9621 });
9622 return expressions;
9623 }
9624 });
9625
9626 OPT(AST_New, function(self, compressor) {
9627 if (compressor.option("sequences")) {
9628 var seq = lift_sequence_in_expression(self, compressor);
9629 if (seq !== self) return seq.optimize(compressor);
9630 }
9631 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
9632 if (compressor.option("unsafe")) {
9633 var exp = self.expression;
9634 if (is_undeclared_ref(exp)) {
9635 switch (exp.name) {
9636 case "Object":
9637 case "RegExp":
9638 case "Function":
9639 case "Error":
9640 case "Array":
9641 return make_node(AST_Call, self, self).transform(compressor);
9642 }
9643 }
9644 }
9645 return self;
9646 });
9647
9648 // (a = b, x && a = c) ---> a = x ? c : b
9649 // (a = b, x || a = c) ---> a = x ? b : c
9650 function to_conditional_assignment(compressor, def, value, node) {
9651 if (!(node instanceof AST_Binary)) return;
9652 if (!(node.operator == "&&" || node.operator == "||")) return;
9653 if (!(node.right instanceof AST_Assign)) return;
9654 if (node.right.operator != "=") return;
9655 if (!(node.right.left instanceof AST_SymbolRef)) return;
9656 if (node.right.left.definition() !== def) return;
9657 if (value.has_side_effects(compressor)) return;
9658 if (!safe_from_assignment(node.left)) return;
9659 if (!safe_from_assignment(node.right.right)) return;
9660 def.replaced++;
9661 return node.operator == "&&" ? make_node(AST_Conditional, node, {
9662 condition: node.left,
9663 consequent: node.right.right,
9664 alternative: value
9665 }) : make_node(AST_Conditional, node, {
9666 condition: node.left,
9667 consequent: value,
9668 alternative: node.right.right
9669 });
9670
9671 function safe_from_assignment(node) {
9672 if (node.has_side_effects(compressor)) return;
9673 var hit = false;
9674 node.walk(new TreeWalker(function(node) {
9675 if (hit) return true;
9676 if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
9677 }));
9678 return !hit;
9679 }
9680 }
9681
9682 OPT(AST_Sequence, function(self, compressor) {
9683 var expressions = filter_for_side_effects();
9684 var end = expressions.length - 1;
9685 merge_conditional_assignments();
9686 trim_right_for_undefined();
9687 if (end == 0) {
9688 self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
9689 if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
9690 return self;
9691 }
9692 self.expressions = expressions;
9693 return self;
9694
9695 function filter_for_side_effects() {
9696 if (!compressor.option("side_effects")) return self.expressions;
9697 var expressions = [];
9698 var first = first_in_statement(compressor);
9699 var last = self.expressions.length - 1;
9700 self.expressions.forEach(function(expr, index) {
9701 if (index < last) expr = expr.drop_side_effect_free(compressor, first);
9702 if (expr) {
9703 merge_sequence(expressions, expr);
9704 first = false;
9705 }
9706 });
9707 return expressions;
9708 }
9709
9710 function trim_right_for_undefined() {
9711 if (!compressor.option("side_effects")) return;
9712 while (end > 0 && is_undefined(expressions[end], compressor)) end--;
9713 if (end < expressions.length - 1) {
9714 expressions[end] = make_node(AST_UnaryPrefix, self, {
9715 operator : "void",
9716 expression : expressions[end]
9717 });
9718 expressions.length = end + 1;
9719 }
9720 }
9721
9722 function merge_conditional_assignments() {
9723 if (!compressor.option("conditionals")) return;
9724 for (var i = 1; i < end; i++) {
9725 var assign = expressions[i - 1];
9726 if (!(assign instanceof AST_Assign)) continue;
9727 if (assign.operator != "=") continue;
9728 if (!(assign.left instanceof AST_SymbolRef)) continue;
9729 var def = assign.left.definition();
9730 var cond = to_conditional_assignment(compressor, def, assign.right, expressions[i]);
9731 if (!cond) continue;
9732 assign.right = cond;
9733 expressions.splice(i, 1);
9734 end--;
9735 }
9736 }
9737 });
9738
9739 OPT(AST_UnaryPostfix, function(self, compressor) {
9740 if (compressor.option("sequences")) {
9741 var seq = lift_sequence_in_expression(self, compressor);
9742 if (seq !== self) return seq.optimize(compressor);
9743 }
9744 return try_evaluate(compressor, self);
9745 });
9746
9747 var SIGN_OPS = makePredicate("+ -");
9748 var MULTIPLICATIVE_OPS = makePredicate("* / %");
9749 OPT(AST_UnaryPrefix, function(self, compressor) {
9750 var op = self.operator;
9751 var exp = self.expression;
9752 if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
9753 return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
9754 }
9755 if (compressor.option("sequences") && can_lift()) {
9756 var seq = lift_sequence_in_expression(self, compressor);
9757 if (seq !== self) return seq.optimize(compressor);
9758 }
9759 if (compressor.option("side_effects") && op == "void") {
9760 exp = exp.drop_side_effect_free(compressor);
9761 if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
9762 self.expression = exp;
9763 return self;
9764 }
9765 if (compressor.option("booleans")) {
9766 if (op == "!" && exp.is_truthy()) {
9767 return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
9768 } else if (compressor.in_boolean_context()) switch (op) {
9769 case "!":
9770 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
9771 // !!foo ---> foo, if we're in boolean context
9772 return exp.expression;
9773 }
9774 if (exp instanceof AST_Binary) {
9775 self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
9776 }
9777 break;
9778 case "typeof":
9779 // typeof always returns a non-empty string, thus it's
9780 // always true in booleans
9781 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
9782 var exprs = [ make_node(AST_True, self) ];
9783 if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
9784 return make_sequence(self, exprs).optimize(compressor);
9785 }
9786 }
9787 if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
9788 if (compressor.option("evaluate")
9789 && exp instanceof AST_Binary
9790 && SIGN_OPS[op]
9791 && MULTIPLICATIVE_OPS[exp.operator]
9792 && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
9793 return make_node(AST_Binary, self, {
9794 operator: exp.operator,
9795 left: make_node(AST_UnaryPrefix, exp.left, {
9796 operator: op,
9797 expression: exp.left
9798 }),
9799 right: exp.right
9800 });
9801 }
9802 // avoids infinite recursion of numerals
9803 return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
9804 ? self : try_evaluate(compressor, self);
9805
9806 function may_not_delete(node) {
9807 return node instanceof AST_Infinity
9808 || node instanceof AST_NaN
9809 || node instanceof AST_NewTarget
9810 || node instanceof AST_PropAccess
9811 || node instanceof AST_SymbolRef
9812 || node instanceof AST_Undefined;
9813 }
9814
9815 function can_lift() {
9816 switch (op) {
9817 case "delete":
9818 return !may_not_delete(exp.tail_node());
9819 case "typeof":
9820 return !is_undeclared_ref(exp.tail_node());
9821 default:
9822 return true;
9823 }
9824 }
9825 });
9826
9827 OPT(AST_Await, function(self, compressor) {
9828 if (!compressor.option("awaits")) return self;
9829 if (compressor.option("sequences")) {
9830 var seq = lift_sequence_in_expression(self, compressor);
9831 if (seq !== self) return seq.optimize(compressor);
9832 }
9833 if (compressor.option("side_effects")) {
9834 var exp = self.expression;
9835 if (exp instanceof AST_Await) return exp;
9836 if (exp instanceof AST_UnaryPrefix) {
9837 if (exp.expression instanceof AST_Await) return exp;
9838 if (exp.operator == "void") return make_node(AST_UnaryPrefix, self, {
9839 operator: "void",
9840 expression: make_node(AST_Await, self, {
9841 expression: exp.expression,
9842 }),
9843 });
9844 }
9845 }
9846 return self;
9847 });
9848
9849 OPT(AST_Yield, function(self, compressor) {
9850 if (!compressor.option("yields")) return self;
9851 if (compressor.option("sequences")) {
9852 var seq = lift_sequence_in_expression(self, compressor);
9853 if (seq !== self) return seq.optimize(compressor);
9854 }
9855 var exp = self.expression;
9856 if (self.nested && exp.TYPE == "Call") {
9857 var inlined = exp.clone().optimize(compressor);
9858 if (inlined.TYPE != "Call") return inlined;
9859 }
9860 return self;
9861 });
9862
9863 AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
9864 if (this.left instanceof AST_PropAccess) {
9865 if (!(this.left.expression instanceof AST_Sequence)) return this;
9866 var x = this.left.expression.expressions.slice();
9867 var e = this.clone();
9868 e.left = e.left.clone();
9869 e.left.expression = x.pop();
9870 x.push(e);
9871 return make_sequence(this, x);
9872 }
9873 if (this.left instanceof AST_Sequence) {
9874 var x = this.left.expressions.slice();
9875 var e = this.clone();
9876 e.left = x.pop();
9877 x.push(e);
9878 return make_sequence(this, x);
9879 }
9880 if (this.right instanceof AST_Sequence) {
9881 if (this.left.has_side_effects(compressor)) return this;
9882 var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
9883 var x = this.right.expressions;
9884 var last = x.length - 1;
9885 for (var i = 0; i < last; i++) {
9886 if (!assign && x[i].has_side_effects(compressor)) break;
9887 }
9888 if (i == last) {
9889 x = x.slice();
9890 var e = this.clone();
9891 e.right = x.pop();
9892 x.push(e);
9893 return make_sequence(this, x);
9894 }
9895 if (i > 0) {
9896 var e = this.clone();
9897 e.right = make_sequence(this.right, x.slice(i));
9898 x = x.slice(0, i);
9899 x.push(e);
9900 return make_sequence(this, x);
9901 }
9902 }
9903 return this;
9904 });
9905
9906 var indexFns = makePredicate("indexOf lastIndexOf");
9907 var commutativeOperators = makePredicate("== === != !== * & | ^");
9908 function is_object(node) {
9909 if (node instanceof AST_Assign) return node.operator == "=" && is_object(node.right);
9910 if (node instanceof AST_Sequence) return is_object(node.tail_node());
9911 if (node instanceof AST_SymbolRef) return is_object(node.fixed_value());
9912 return node instanceof AST_Array
9913 || node instanceof AST_Class
9914 || node instanceof AST_Lambda
9915 || node instanceof AST_New
9916 || node instanceof AST_Object;
9917 }
9918
9919 function is_primitive(compressor, node) {
9920 if (node.is_constant()) return true;
9921 if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right);
9922 if (node instanceof AST_Binary) {
9923 return !lazy_op[node.operator]
9924 || is_primitive(compressor, node.left) && is_primitive(compressor, node.right);
9925 }
9926 if (node instanceof AST_Conditional) {
9927 return is_primitive(compressor, node.consequent) && is_primitive(compressor, node.alternative);
9928 }
9929 if (node instanceof AST_Sequence) return is_primitive(compressor, node.tail_node());
9930 if (node instanceof AST_SymbolRef) {
9931 var fixed = node.fixed_value();
9932 return fixed && is_primitive(compressor, fixed);
9933 }
9934 if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag);
9935 if (node instanceof AST_Unary) return true;
9936 }
9937
9938 function repeatable(compressor, node) {
9939 if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
9940 if (node instanceof AST_Sub) {
9941 return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
9942 }
9943 if (node instanceof AST_Symbol) return true;
9944 return !node.has_side_effects(compressor);
9945 }
9946
9947 OPT(AST_Binary, function(self, compressor) {
9948 function reversible() {
9949 return self.left.is_constant()
9950 || self.right.is_constant()
9951 || !self.left.has_side_effects(compressor)
9952 && !self.right.has_side_effects(compressor);
9953 }
9954 function reverse(op) {
9955 if (reversible()) {
9956 if (op) self.operator = op;
9957 var tmp = self.left;
9958 self.left = self.right;
9959 self.right = tmp;
9960 }
9961 }
9962 function swap_chain() {
9963 var rhs = self.right;
9964 self.left = make_node(AST_Binary, self, {
9965 operator: self.operator,
9966 left: self.left,
9967 right: rhs.left,
9968 start: self.left.start,
9969 end: rhs.left.end
9970 });
9971 self.right = rhs.right;
9972 self.left = self.left.transform(compressor);
9973 }
9974 if (commutativeOperators[self.operator]
9975 && self.right.is_constant()
9976 && !self.left.is_constant()
9977 && !(self.left instanceof AST_Binary
9978 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
9979 // if right is a constant, whatever side effects the
9980 // left side might have could not influence the
9981 // result. hence, force switch.
9982 reverse();
9983 }
9984 if (compressor.option("sequences")) {
9985 var seq = self.lift_sequences(compressor);
9986 if (seq !== self) return seq.optimize(compressor);
9987 }
9988 if (compressor.option("assignments") && lazy_op[self.operator]) {
9989 var assign = self.right;
9990 // a || (a = x) ---> a = a || x
9991 // a && (a = x) ---> a = a && x
9992 if (self.left instanceof AST_SymbolRef
9993 && assign instanceof AST_Assign
9994 && assign.operator == "="
9995 && self.left.equivalent_to(assign.left)) {
9996 self.right = assign.right;
9997 assign.right = self;
9998 return assign;
9999 }
10000 }
10001 if (compressor.option("comparisons")) switch (self.operator) {
10002 case "===":
10003 case "!==":
10004 if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
10005 AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
10006 return make_sequence(self, [
10007 self.right,
10008 make_node(self.operator == "===" ? AST_False : AST_True, self)
10009 ]).optimize(compressor);
10010 }
10011 var is_strict_comparison = true;
10012 if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
10013 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
10014 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
10015 repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
10016 self.operator = self.operator.slice(0, 2);
10017 }
10018 // XXX: intentionally falling down to the next case
10019 case "==":
10020 case "!=":
10021 // void 0 == x ---> null == x
10022 if (!is_strict_comparison && is_undefined(self.left, compressor)) {
10023 self.left = make_node(AST_Null, self.left);
10024 }
10025 // "undefined" == typeof x ---> undefined === x
10026 else if (compressor.option("typeofs")
10027 && self.left instanceof AST_String
10028 && self.left.value == "undefined"
10029 && self.right instanceof AST_UnaryPrefix
10030 && self.right.operator == "typeof") {
10031 var expr = self.right.expression;
10032 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
10033 : !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
10034 self.right = expr;
10035 self.left = make_node(AST_Undefined, self.left).optimize(compressor);
10036 if (self.operator.length == 2) self.operator += "=";
10037 }
10038 }
10039 // obj !== obj ---> false
10040 else if (self.left instanceof AST_SymbolRef
10041 && self.right instanceof AST_SymbolRef
10042 && self.left.definition() === self.right.definition()
10043 && is_object(self.left)) {
10044 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
10045 }
10046 break;
10047 case "&&":
10048 case "||":
10049 // void 0 !== x && null !== x ---> null != x
10050 // void 0 === x || null === x ---> null == x
10051 var lhs = self.left;
10052 if (lhs.operator == self.operator) {
10053 lhs = lhs.right;
10054 }
10055 if (lhs instanceof AST_Binary
10056 && lhs.operator == (self.operator == "&&" ? "!==" : "===")
10057 && self.right instanceof AST_Binary
10058 && lhs.operator == self.right.operator
10059 && (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
10060 || lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
10061 && !lhs.right.has_side_effects(compressor)
10062 && lhs.right.equivalent_to(self.right.right)) {
10063 var combined = make_node(AST_Binary, self, {
10064 operator: lhs.operator.slice(0, -1),
10065 left: make_node(AST_Null, self),
10066 right: lhs.right
10067 });
10068 if (lhs !== self.left) {
10069 combined = make_node(AST_Binary, self, {
10070 operator: self.operator,
10071 left: self.left.left,
10072 right: combined
10073 });
10074 }
10075 return combined;
10076 }
10077 break;
10078 }
10079 var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
10080 if (in_bool) switch (self.operator) {
10081 case "+":
10082 var ll = self.left.evaluate(compressor);
10083 var rr = self.right.evaluate(compressor);
10084 if (ll && typeof ll == "string") {
10085 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10086 return make_sequence(self, [
10087 self.right,
10088 make_node(AST_True, self)
10089 ]).optimize(compressor);
10090 }
10091 if (rr && typeof rr == "string") {
10092 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10093 return make_sequence(self, [
10094 self.left,
10095 make_node(AST_True, self)
10096 ]).optimize(compressor);
10097 }
10098 break;
10099 case "==":
10100 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10101 return make_node(AST_UnaryPrefix, self, {
10102 operator: "!",
10103 expression: self.right
10104 }).optimize(compressor);
10105 }
10106 break;
10107 case "!=":
10108 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10109 return self.right.optimize(compressor);
10110 }
10111 break;
10112 }
10113 var parent = compressor.parent();
10114 if (compressor.option("comparisons") && self.is_boolean(compressor)) {
10115 if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
10116 var negated = best_of(compressor, self, make_node(AST_UnaryPrefix, self, {
10117 operator: "!",
10118 expression: self.negate(compressor, first_in_statement(compressor))
10119 }));
10120 if (negated !== self) return negated;
10121 }
10122 switch (self.operator) {
10123 case ">": reverse("<"); break;
10124 case ">=": reverse("<="); break;
10125 }
10126 }
10127 // x && (y && z) ---> x && y && z
10128 // x || (y || z) ---> x || y || z
10129 if (compressor.option("conditionals")
10130 && lazy_op[self.operator]
10131 && self.right instanceof AST_Binary
10132 && self.operator == self.right.operator) {
10133 swap_chain();
10134 }
10135 if (compressor.option("strings") && self.operator == "+") {
10136 // "foo" + 42 + "" ---> "foo" + 42
10137 if (self.right instanceof AST_String
10138 && self.right.value == ""
10139 && self.left.is_string(compressor)) {
10140 return self.left.optimize(compressor);
10141 }
10142 // "" + ("foo" + 42) ---> "foo" + 42
10143 if (self.left instanceof AST_String
10144 && self.left.value == ""
10145 && self.right.is_string(compressor)) {
10146 return self.right.optimize(compressor);
10147 }
10148 // "" + 42 + "foo" ---> 42 + "foo"
10149 if (self.left instanceof AST_Binary
10150 && self.left.operator == "+"
10151 && self.left.left instanceof AST_String
10152 && self.left.left.value == ""
10153 && self.right.is_string(compressor)) {
10154 self.left = self.left.right;
10155 return self.optimize(compressor);
10156 }
10157 // "x" + (y + "z") ---> "x" + y + "z"
10158 // x + ("y" + z) ---> x + "y" + z
10159 if (self.right instanceof AST_Binary
10160 && self.operator == self.right.operator
10161 && (self.left.is_string(compressor) && self.right.is_string(compressor)
10162 || self.right.left.is_string(compressor)
10163 && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
10164 swap_chain();
10165 }
10166 }
10167 if (compressor.option("evaluate")) {
10168 var associative = true;
10169 switch (self.operator) {
10170 case "&&":
10171 var ll = fuzzy_eval(self.left);
10172 if (!ll) {
10173 AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
10174 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10175 } else if (!(ll instanceof AST_Node)) {
10176 AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
10177 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10178 }
10179 var rr = self.right.evaluate(compressor);
10180 if (!rr) {
10181 if (in_bool) {
10182 AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
10183 return make_sequence(self, [
10184 self.left,
10185 make_node(AST_False, self)
10186 ]).optimize(compressor);
10187 } else self.falsy = true;
10188 } else if (!(rr instanceof AST_Node)) {
10189 if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
10190 AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
10191 return self.left.optimize(compressor);
10192 }
10193 }
10194 // (x || false) && y ---> x ? y : false
10195 if (self.left.operator == "||") {
10196 var lr = self.left.right.evaluate(compressor, true);
10197 if (!lr) return make_node(AST_Conditional, self, {
10198 condition: self.left.left,
10199 consequent: self.right,
10200 alternative: self.left.right
10201 }).optimize(compressor);
10202 }
10203 break;
10204 case "??":
10205 var nullish = true;
10206 case "||":
10207 var ll = fuzzy_eval(self.left, nullish);
10208 if (nullish ? ll == null : !ll) {
10209 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10210 operator: self.operator,
10211 value: nullish ? "nulish" : "false",
10212 file: self.start.file,
10213 line: self.start.line,
10214 col: self.start.col,
10215 });
10216 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10217 } else if (!(ll instanceof AST_Node)) {
10218 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10219 operator: self.operator,
10220 value: nullish ? "defined" : "true",
10221 file: self.start.file,
10222 line: self.start.line,
10223 col: self.start.col,
10224 });
10225 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10226 }
10227 var rr = self.right.evaluate(compressor);
10228 if (!rr) {
10229 if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
10230 AST_Node.warn("Dropping side-effect-free {operator} [{file}:{line},{col}]", {
10231 operator: self.operator,
10232 file: self.start.file,
10233 line: self.start.line,
10234 col: self.start.col,
10235 });
10236 return self.left.optimize(compressor);
10237 }
10238 } else if (!nullish && !(rr instanceof AST_Node)) {
10239 if (in_bool) {
10240 AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
10241 return make_sequence(self, [
10242 self.left,
10243 make_node(AST_True, self)
10244 ]).optimize(compressor);
10245 } else self.truthy = true;
10246 }
10247 // x && true || y ---> x ? true : y
10248 if (!nullish && self.left.operator == "&&") {
10249 var lr = self.left.right.is_truthy() || self.left.right.evaluate(compressor, true);
10250 if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
10251 condition: self.left.left,
10252 consequent: self.left.right,
10253 alternative: self.right
10254 }).optimize(compressor);
10255 }
10256 break;
10257 case "+":
10258 // "foo" + ("bar" + x) ---> "foobar" + x
10259 if (self.left instanceof AST_Constant
10260 && self.right instanceof AST_Binary
10261 && self.right.operator == "+"
10262 && self.right.left instanceof AST_Constant
10263 && self.right.is_string(compressor)) {
10264 self = make_node(AST_Binary, self, {
10265 operator: "+",
10266 left: make_node(AST_String, self.left, {
10267 value: "" + self.left.value + self.right.left.value,
10268 start: self.left.start,
10269 end: self.right.left.end
10270 }),
10271 right: self.right.right
10272 });
10273 }
10274 // (x + "foo") + "bar" ---> x + "foobar"
10275 if (self.right instanceof AST_Constant
10276 && self.left instanceof AST_Binary
10277 && self.left.operator == "+"
10278 && self.left.right instanceof AST_Constant
10279 && self.left.is_string(compressor)) {
10280 self = make_node(AST_Binary, self, {
10281 operator: "+",
10282 left: self.left.left,
10283 right: make_node(AST_String, self.right, {
10284 value: "" + self.left.right.value + self.right.value,
10285 start: self.left.right.start,
10286 end: self.right.end
10287 })
10288 });
10289 }
10290 // a + -b ---> a - b
10291 if (self.right instanceof AST_UnaryPrefix
10292 && self.right.operator == "-"
10293 && self.left.is_number(compressor)) {
10294 self = make_node(AST_Binary, self, {
10295 operator: "-",
10296 left: self.left,
10297 right: self.right.expression
10298 });
10299 break;
10300 }
10301 // -a + b ---> b - a
10302 if (self.left instanceof AST_UnaryPrefix
10303 && self.left.operator == "-"
10304 && reversible()
10305 && self.right.is_number(compressor)) {
10306 self = make_node(AST_Binary, self, {
10307 operator: "-",
10308 left: self.right,
10309 right: self.left.expression
10310 });
10311 break;
10312 }
10313 // (a + b) + 3 ---> 3 + (a + b)
10314 if (compressor.option("unsafe_math")
10315 && self.left instanceof AST_Binary
10316 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10317 && self.right.is_constant()
10318 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10319 && self.left.is_number(compressor)
10320 && !self.left.right.is_constant()
10321 && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
10322 self = make_node(AST_Binary, self, {
10323 operator: self.left.operator,
10324 left: make_node(AST_Binary, self, {
10325 operator: self.operator,
10326 left: self.right,
10327 right: self.left.left
10328 }),
10329 right: self.left.right
10330 });
10331 break;
10332 }
10333 case "-":
10334 // a - -b ---> a + b
10335 if (self.right instanceof AST_UnaryPrefix
10336 && self.right.operator == "-"
10337 && self.left.is_number(compressor)
10338 && self.right.expression.is_number(compressor)) {
10339 self = make_node(AST_Binary, self, {
10340 operator: "+",
10341 left: self.left,
10342 right: self.right.expression
10343 });
10344 break;
10345 }
10346 case "*":
10347 case "/":
10348 associative = compressor.option("unsafe_math");
10349 // +a - b ---> a - b
10350 // a - +b ---> a - b
10351 if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
10352 var node = self[operand];
10353 if (node instanceof AST_UnaryPrefix && node.operator == "+") {
10354 var exp = node.expression;
10355 if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) {
10356 self[operand] = exp;
10357 }
10358 }
10359 });
10360 case "&":
10361 case "|":
10362 case "^":
10363 // a + +b ---> +b + a
10364 if (self.operator != "-"
10365 && self.operator != "/"
10366 && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
10367 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10368 && reversible()
10369 && !(self.left instanceof AST_Binary
10370 && self.left.operator != self.operator
10371 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10372 var reversed = make_node(AST_Binary, self, {
10373 operator: self.operator,
10374 left: self.right,
10375 right: self.left
10376 });
10377 if (self.right instanceof AST_Constant
10378 && !(self.left instanceof AST_Constant)) {
10379 self = best_of(compressor, reversed, self);
10380 } else {
10381 self = best_of(compressor, self, reversed);
10382 }
10383 }
10384 if (!associative || !self.is_number(compressor)) break;
10385 // a + (b + c) ---> (a + b) + c
10386 if (self.right instanceof AST_Binary
10387 && self.right.operator != "%"
10388 && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
10389 && self.right.is_number(compressor)
10390 && (self.operator != "+"
10391 || self.right.left.is_boolean(compressor)
10392 || self.right.left.is_number(compressor))
10393 && (self.operator != "-" || !self.left.is_negative_zero())
10394 && (self.right.left.is_constant_expression()
10395 || !self.right.right.has_side_effects(compressor))
10396 && !is_modify_array(self.right.right)) {
10397 self = make_node(AST_Binary, self, {
10398 operator: align(self.operator, self.right.operator),
10399 left: make_node(AST_Binary, self.left, {
10400 operator: self.operator,
10401 left: self.left,
10402 right: self.right.left,
10403 start: self.left.start,
10404 end: self.right.left.end
10405 }),
10406 right: self.right.right
10407 });
10408 if (self.operator == "+"
10409 && !self.right.is_boolean(compressor)
10410 && !self.right.is_number(compressor)) {
10411 self.right = make_node(AST_UnaryPrefix, self.right, {
10412 operator: "+",
10413 expression: self.right
10414 });
10415 }
10416 }
10417 // (2 * n) * 3 ---> 6 * n
10418 // (n + 2) + 3 ---> n + 5
10419 if (self.right instanceof AST_Constant
10420 && self.left instanceof AST_Binary
10421 && self.left.operator != "%"
10422 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10423 && self.left.is_number(compressor)) {
10424 if (self.left.left instanceof AST_Constant) {
10425 var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
10426 self = make_binary(self, self.left.operator, try_evaluate(compressor, lhs), self.left.right);
10427 } else if (self.left.right instanceof AST_Constant) {
10428 var op = align(self.left.operator, self.operator);
10429 var rhs = try_evaluate(compressor, make_binary(self.left, op, self.left.right, self.right));
10430 if (rhs.is_constant()
10431 && !(self.left.operator == "-"
10432 && self.right.value != 0
10433 && +rhs.value == 0
10434 && self.left.left.is_negative_zero())) {
10435 self = make_binary(self, self.left.operator, self.left.left, rhs);
10436 }
10437 }
10438 }
10439 break;
10440 }
10441 if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
10442 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
10443 // 0 + n ---> n
10444 case "+":
10445 if (self.left.value == 0) {
10446 if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
10447 operator: "+",
10448 expression: self.right
10449 }).optimize(compressor);
10450 if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
10451 }
10452 break;
10453 // 1 * n ---> n
10454 case "*":
10455 if (self.left.value == 1) {
10456 return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
10457 operator: "+",
10458 expression: self.right
10459 }).optimize(compressor);
10460 }
10461 break;
10462 }
10463 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
10464 // n + 0 ---> n
10465 case "+":
10466 if (self.right.value == 0) {
10467 if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
10468 operator: "+",
10469 expression: self.left
10470 }).optimize(compressor);
10471 if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
10472 }
10473 break;
10474 // n - 0 ---> n
10475 case "-":
10476 if (self.right.value == 0) {
10477 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
10478 operator: "+",
10479 expression: self.left
10480 }).optimize(compressor);
10481 }
10482 break;
10483 // n / 1 ---> n
10484 case "/":
10485 if (self.right.value == 1) {
10486 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
10487 operator: "+",
10488 expression: self.left
10489 }).optimize(compressor);
10490 }
10491 break;
10492 }
10493 }
10494 }
10495 if (compressor.option("typeofs")) switch (self.operator) {
10496 case "&&":
10497 mark_locally_defined(self.left, self.right, null);
10498 break;
10499 case "||":
10500 mark_locally_defined(self.left, null, self.right);
10501 break;
10502 }
10503 if (compressor.option("unsafe")) {
10504 var indexRight = is_indexFn(self.right);
10505 if (in_bool
10506 && indexRight
10507 && (self.operator == "==" || self.operator == "!=")
10508 && self.left instanceof AST_Number
10509 && self.left.value == 0) {
10510 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
10511 operator: "!",
10512 expression: self.right
10513 }) : self.right).optimize(compressor);
10514 }
10515 var indexLeft = is_indexFn(self.left);
10516 if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
10517 var node = make_node(AST_UnaryPrefix, self, {
10518 operator: "!",
10519 expression: make_node(AST_UnaryPrefix, self, {
10520 operator: "~",
10521 expression: indexLeft ? self.left : self.right
10522 })
10523 });
10524 switch (self.operator) {
10525 case "<":
10526 if (indexLeft) break;
10527 case "<=":
10528 case "!=":
10529 node = make_node(AST_UnaryPrefix, self, {
10530 operator: "!",
10531 expression: node
10532 });
10533 break;
10534 }
10535 return node.optimize(compressor);
10536 }
10537 }
10538 return try_evaluate(compressor, self);
10539
10540 function is_modify_array(node) {
10541 var found = false;
10542 node.walk(new TreeWalker(function(node) {
10543 if (found) return true;
10544 if (node instanceof AST_Assign) {
10545 if (node.left instanceof AST_PropAccess) return found = true;
10546 } else if (node instanceof AST_Unary) {
10547 if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) {
10548 return found = true;
10549 }
10550 }
10551 }));
10552 return found;
10553 }
10554
10555 function align(ref, op) {
10556 switch (ref) {
10557 case "-":
10558 return op == "+" ? "-" : "+";
10559 case "/":
10560 return op == "*" ? "/" : "*";
10561 default:
10562 return op;
10563 }
10564 }
10565
10566 function make_binary(orig, op, left, right, start, end) {
10567 if (op == "+") {
10568 if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
10569 left = make_node(AST_UnaryPrefix, left, {
10570 operator: "+",
10571 expression: left
10572 });
10573 }
10574 if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
10575 right = make_node(AST_UnaryPrefix, right, {
10576 operator: "+",
10577 expression: right
10578 });
10579 }
10580 }
10581 return make_node(AST_Binary, orig, {
10582 operator: op,
10583 left: left,
10584 right: right,
10585 start: start,
10586 end: end
10587 });
10588 }
10589
10590 function fuzzy_eval(node, nullish) {
10591 if (node.truthy) return true;
10592 if (node.falsy && !nullish) return false;
10593 if (node.is_truthy()) return true;
10594 return node.evaluate(compressor, true);
10595 }
10596
10597 function is_indexFn(node) {
10598 return node.TYPE == "Call"
10599 && node.expression instanceof AST_Dot
10600 && indexFns[node.expression.property];
10601 }
10602
10603 function is_indexOf_match_pattern() {
10604 switch (self.operator) {
10605 case "<=":
10606 // 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
10607 return indexRight && self.left instanceof AST_Number && self.left.value == 0;
10608 case "<":
10609 // array.indexOf(string) < 0 ---> !~array.indexOf(string)
10610 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
10611 // -1 < array.indexOf(string) ---> !!~array.indexOf(string)
10612 case "==":
10613 case "!=":
10614 // -1 == array.indexOf(string) ---> !~array.indexOf(string)
10615 // -1 != array.indexOf(string) ---> !!~array.indexOf(string)
10616 if (!indexRight) return false;
10617 return self.left instanceof AST_Number && self.left.value == -1
10618 || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
10619 && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
10620 }
10621 }
10622 });
10623
10624 OPT(AST_SymbolExport, function(self) {
10625 return self;
10626 });
10627
10628 function recursive_ref(compressor, def) {
10629 var level = 0, node = compressor.self();
10630 do {
10631 if (is_lambda(node) && node.name && node.name.definition() === def) return node;
10632 } while (node = compressor.parent(level++));
10633 }
10634
10635 function same_scope(def) {
10636 var scope = def.scope.resolve();
10637 return all(def.references, function(ref) {
10638 return scope === ref.scope.resolve();
10639 });
10640 }
10641
10642 OPT(AST_SymbolRef, function(self, compressor) {
10643 if (!compressor.option("ie8")
10644 && is_undeclared_ref(self)
10645 // testing against `self.scope.uses_with` is an optimization
10646 && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
10647 switch (self.name) {
10648 case "undefined":
10649 return make_node(AST_Undefined, self).optimize(compressor);
10650 case "NaN":
10651 return make_node(AST_NaN, self).optimize(compressor);
10652 case "Infinity":
10653 return make_node(AST_Infinity, self).optimize(compressor);
10654 }
10655 }
10656 var parent = compressor.parent();
10657 if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
10658 var def = self.definition();
10659 var fixed = self.fixed_value();
10660 var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
10661 if (single_use) {
10662 if (is_lambda(fixed)) {
10663 if ((def.scope !== self.scope.resolve() || def.in_loop)
10664 && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
10665 single_use = false;
10666 } else if (recursive_ref(compressor, def)) {
10667 single_use = false;
10668 } else if (fixed.name && fixed.name.definition() !== def) {
10669 single_use = false;
10670 } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
10671 single_use = fixed.is_constant_expression(self.scope);
10672 if (single_use == "f") {
10673 var scope = self.scope;
10674 do {
10675 if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
10676 scope.inlined = true;
10677 }
10678 } while (scope = scope.parent_scope);
10679 }
10680 } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
10681 || fixed.name.name == "yield" && is_generator(fixed))) {
10682 single_use = false;
10683 } else if (fixed.has_side_effects(compressor)) {
10684 single_use = false;
10685 } else if (compressor.option("ie8") && fixed instanceof AST_Class) {
10686 single_use = false;
10687 }
10688 if (single_use) fixed.parent_scope = self.scope;
10689 } else if (!fixed || !fixed.is_constant_expression() || fixed.drop_side_effect_free(compressor)) {
10690 single_use = false;
10691 }
10692 }
10693 if (single_use) {
10694 def.single_use = false;
10695 fixed._squeezed = true;
10696 fixed.single_use = true;
10697 if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
10698 if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
10699 if (is_lambda(fixed)) {
10700 var scope = self.scope.resolve();
10701 fixed.enclosed.forEach(function(def) {
10702 if (fixed.variables.has(def.name)) return;
10703 if (scope.var_names()[def.name]) return;
10704 scope.enclosed.push(def);
10705 scope.var_names()[def.name] = true;
10706 });
10707 }
10708 var value;
10709 if (def.recursive_refs > 0) {
10710 value = fixed.clone(true);
10711 var defun_def = value.name.definition();
10712 var lambda_def = value.variables.get(value.name.name);
10713 var name = lambda_def && lambda_def.orig[0];
10714 var def_fn_name, symbol_type;
10715 if (value instanceof AST_Class) {
10716 def_fn_name = "def_function";
10717 symbol_type = AST_SymbolClass;
10718 } else {
10719 def_fn_name = "def_variable";
10720 symbol_type = AST_SymbolLambda;
10721 }
10722 if (!(name instanceof symbol_type)) {
10723 name = make_node(symbol_type, value.name, value.name);
10724 name.scope = value;
10725 value.name = name;
10726 lambda_def = value[def_fn_name](name);
10727 lambda_def.recursive_refs = def.recursive_refs;
10728 }
10729 value.walk(new TreeWalker(function(node) {
10730 if (node instanceof AST_SymbolDeclaration) {
10731 if (node !== name) {
10732 var def = node.definition();
10733 def.orig.push(node);
10734 def.eliminated++;
10735 }
10736 return;
10737 }
10738 if (!(node instanceof AST_SymbolRef)) return;
10739 var def = node.definition();
10740 if (def === defun_def) {
10741 node.thedef = lambda_def;
10742 lambda_def.references.push(node);
10743 } else {
10744 def.single_use = false;
10745 var fn = node.fixed_value();
10746 if (!is_lambda(fn)) return;
10747 if (!fn.name) return;
10748 if (fn.name.definition() !== def) return;
10749 if (def.scope !== fn.name.scope) return;
10750 if (fixed.variables.get(fn.name.name) !== def) return;
10751 fn.name = fn.name.clone();
10752 var value_def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
10753 node.thedef = value_def;
10754 value_def.references.push(node);
10755 }
10756 }));
10757 } else {
10758 if (fixed instanceof AST_Scope) {
10759 compressor.push(fixed);
10760 value = fixed.optimize(compressor);
10761 compressor.pop();
10762 } else {
10763 value = fixed.optimize(compressor);
10764 }
10765 value = value.transform(new TreeTransformer(function(node, descend) {
10766 if (node instanceof AST_Scope) return node;
10767 node = node.clone();
10768 descend(node, this);
10769 return node;
10770 }));
10771 }
10772 def.replaced++;
10773 return value;
10774 }
10775 var local = self.fixed !== def.fixed;
10776 if (fixed && (local || def.should_replace !== false)) {
10777 var init;
10778 if (fixed instanceof AST_This) {
10779 if (!is_funarg(def) && same_scope(def)) {
10780 init = fixed;
10781 }
10782 } else {
10783 var ev = fixed.evaluate(compressor, true);
10784 if (ev !== fixed
10785 && typeof ev != "function"
10786 && (typeof ev != "object"
10787 || ev instanceof RegExp
10788 && compressor.option("unsafe_regexp")
10789 && !def.cross_loop && same_scope(def))) {
10790 init = make_node_from_constant(ev, fixed);
10791 }
10792 }
10793 if (init) {
10794 if (!local && def.should_replace === undefined) {
10795 var value_length = init.optimize(compressor).print_to_string().length;
10796 if (!has_symbol_ref(fixed)) {
10797 value_length = Math.min(value_length, fixed.print_to_string().length);
10798 }
10799 var name_length = def.name.length;
10800 if (compressor.option("unused") && !compressor.exposed(def)) {
10801 var referenced = def.references.length - def.replaced;
10802 name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
10803 }
10804 var delta = value_length - Math.floor(name_length);
10805 def.should_replace = delta < compressor.eval_threshold;
10806 }
10807 if (local || def.should_replace) {
10808 var value;
10809 if (has_symbol_ref(fixed)) {
10810 value = init.optimize(compressor);
10811 if (value === init) value = value.clone(true);
10812 } else {
10813 value = best_of_expression(init.optimize(compressor), fixed);
10814 if (value === init || value === fixed) value = value.clone(true);
10815 }
10816 def.replaced++;
10817 return value;
10818 }
10819 }
10820 }
10821 }
10822 return self;
10823
10824 function has_symbol_ref(value) {
10825 var found;
10826 value.walk(new TreeWalker(function(node) {
10827 if (node instanceof AST_SymbolRef) found = true;
10828 if (found) return true;
10829 }));
10830 return found;
10831 }
10832 });
10833
10834 function is_raw_tag(compressor, tag) {
10835 return compressor.option("unsafe")
10836 && tag instanceof AST_Dot
10837 && tag.property == "raw"
10838 && is_undeclared_ref(tag.expression)
10839 && tag.expression.name == "String";
10840 }
10841
10842 OPT(AST_Template, function(self, compressor) {
10843 if (!compressor.option("templates")) return self;
10844 var tag = self.tag;
10845 if (!tag || is_raw_tag(compressor, tag)) {
10846 var exprs = self.expressions.slice();
10847 var strs = self.strings.slice();
10848 var CHANGED = false;
10849 for (var i = exprs.length; --i >= 0;) {
10850 var node = exprs[i];
10851 var ev = node.evaluate(compressor);
10852 if (ev === node) continue;
10853 if (tag && /\r|\\|`/.test(ev)) continue;
10854 ev = ("" + ev).replace(/\r|\\|`/g, function(s) {
10855 return "\\" + (s == "\r" ? "r" : s);
10856 });
10857 if (ev.length > node.print_to_string().length + 3) continue;
10858 var combined = strs[i] + ev + strs[i + 1];
10859 if (typeof make_node(AST_Template, self, {
10860 expressions: [],
10861 strings: [ combined ],
10862 tag: tag,
10863 }).evaluate(compressor) != typeof make_node(AST_Template, self, {
10864 expressions: [ node ],
10865 strings: strs.slice(i, i + 2),
10866 tag: tag,
10867 }).evaluate(compressor)) continue;
10868 exprs.splice(i, 1);
10869 strs.splice(i, 2, combined);
10870 CHANGED = true;
10871 }
10872 if (CHANGED) {
10873 self.expressions = exprs;
10874 self.strings = strs;
10875 }
10876 }
10877 return try_evaluate(compressor, self);
10878 });
10879
10880 function is_atomic(lhs, self) {
10881 return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
10882 }
10883
10884 OPT(AST_Undefined, function(self, compressor) {
10885 if (compressor.option("unsafe_undefined")) {
10886 var undef = find_scope(compressor).find_variable("undefined");
10887 if (undef) {
10888 var ref = make_node(AST_SymbolRef, self, {
10889 name : "undefined",
10890 scope : undef.scope,
10891 thedef : undef
10892 });
10893 ref.is_undefined = true;
10894 return ref;
10895 }
10896 }
10897 var lhs = is_lhs(compressor.self(), compressor.parent());
10898 if (lhs && is_atomic(lhs, self)) return self;
10899 return make_node(AST_UnaryPrefix, self, {
10900 operator: "void",
10901 expression: make_node(AST_Number, self, {
10902 value: 0
10903 })
10904 });
10905 });
10906
10907 OPT(AST_Infinity, function(self, compressor) {
10908 var lhs = is_lhs(compressor.self(), compressor.parent());
10909 if (lhs && is_atomic(lhs, self)) return self;
10910 if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) {
10911 return self;
10912 }
10913 return make_node(AST_Binary, self, {
10914 operator: "/",
10915 left: make_node(AST_Number, self, {
10916 value: 1
10917 }),
10918 right: make_node(AST_Number, self, {
10919 value: 0
10920 })
10921 });
10922 });
10923
10924 OPT(AST_NaN, function(self, compressor) {
10925 var lhs = is_lhs(compressor.self(), compressor.parent());
10926 if (lhs && is_atomic(lhs, self)) return self;
10927 if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
10928 return make_node(AST_Binary, self, {
10929 operator: "/",
10930 left: make_node(AST_Number, self, {
10931 value: 0
10932 }),
10933 right: make_node(AST_Number, self, {
10934 value: 0
10935 })
10936 });
10937 });
10938
10939 function is_reachable(self, defs) {
10940 var reachable = false;
10941 var find_ref = new TreeWalker(function(node) {
10942 if (reachable) return true;
10943 if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
10944 return reachable = true;
10945 }
10946 });
10947 var scan_scope = new TreeWalker(function(node) {
10948 if (reachable) return true;
10949 if (node instanceof AST_Scope && node !== self) {
10950 var parent = scan_scope.parent();
10951 if (parent instanceof AST_Call && parent.expression === node) return;
10952 node.walk(find_ref);
10953 return true;
10954 }
10955 });
10956 self.walk(scan_scope);
10957 return reachable;
10958 }
10959
10960 var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
10961 var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
10962 OPT(AST_Assign, function(self, compressor) {
10963 if (compressor.option("dead_code")) {
10964 if (self.left instanceof AST_PropAccess) {
10965 if (self.operator == "=") {
10966 if (self.__drop) {
10967 var exprs = [ self.left.expression ];
10968 if (self.left instanceof AST_Sub) exprs.push(self.left.property);
10969 exprs.push(self.right);
10970 return make_sequence(self, exprs).optimize(compressor);
10971 }
10972 if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
10973 return self.right;
10974 }
10975 var exp = self.left.expression;
10976 if (exp instanceof AST_Lambda
10977 || !compressor.has_directive("use strict")
10978 && exp instanceof AST_Constant
10979 && !exp.may_throw_on_access(compressor)) {
10980 return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
10981 self.left.property,
10982 self.right
10983 ]).optimize(compressor);
10984 }
10985 }
10986 } else if (self.left instanceof AST_SymbolRef && all(self.left.definition().orig, function(sym) {
10987 return !(sym instanceof AST_SymbolConst);
10988 })) {
10989 var parent;
10990 if (self.operator == "=" && self.left.equivalent_to(self.right)
10991 && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
10992 return self.right;
10993 }
10994 if (self.left.is_immutable()) return strip_assignment();
10995 var def = self.left.definition();
10996 var scope = def.scope.resolve();
10997 var local = scope === compressor.find_parent(AST_Lambda);
10998 var level = 0, node;
10999 parent = compressor.self();
11000 do {
11001 node = parent;
11002 parent = compressor.parent(level++);
11003 if (parent instanceof AST_Assign) {
11004 var found = false;
11005 if (parent.left.match_symbol(function(node) {
11006 if (node instanceof AST_PropAccess) return true;
11007 if (!found && node instanceof AST_SymbolRef && node.definition() === def) {
11008 if (in_try(level, parent)) return true;
11009 def.fixed = false;
11010 found = true;
11011 }
11012 })) break;
11013 if (found) return strip_assignment();
11014 } else if (parent instanceof AST_Exit) {
11015 if (!local) break;
11016 if (in_try(level, parent)) break;
11017 if (is_reachable(scope, [ def ])) break;
11018 def.fixed = false;
11019 return strip_assignment();
11020 } else if (parent instanceof AST_VarDef) {
11021 if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
11022 if (parent.name.definition() !== def) continue;
11023 if (in_try(level, parent)) break;
11024 def.fixed = false;
11025 return strip_assignment();
11026 }
11027 } while (parent instanceof AST_Binary && parent.right === node
11028 || parent instanceof AST_Conditional && parent.condition !== node
11029 || parent instanceof AST_Sequence && parent.tail_node() === node
11030 || parent instanceof AST_UnaryPrefix);
11031 }
11032 }
11033 if (compressor.option("sequences")) {
11034 var seq = self.lift_sequences(compressor);
11035 if (seq !== self) return seq.optimize(compressor);
11036 }
11037 if (compressor.option("assignments")) {
11038 if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
11039 // x = expr1 OP expr2
11040 if (self.right.left instanceof AST_SymbolRef
11041 && self.right.left.name == self.left.name
11042 && ASSIGN_OPS[self.right.operator]) {
11043 // x = x - 2 ---> x -= 2
11044 return make_node(AST_Assign, self, {
11045 operator: self.right.operator + "=",
11046 left: self.left,
11047 right: self.right.right,
11048 });
11049 }
11050 if (self.right.right instanceof AST_SymbolRef
11051 && self.right.right.name == self.left.name
11052 && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
11053 && !self.right.left.has_side_effects(compressor)) {
11054 // x = 2 & x ---> x &= 2
11055 return make_node(AST_Assign, self, {
11056 operator: self.right.operator + "=",
11057 left: self.left,
11058 right: self.right.left,
11059 });
11060 }
11061 }
11062 if ((self.operator == "-=" || self.operator == "+="
11063 && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
11064 && self.right instanceof AST_Number
11065 && self.right.value == 1) {
11066 var op = self.operator.slice(0, -1);
11067 return make_node(AST_UnaryPrefix, self, {
11068 operator: op + op,
11069 expression: self.left
11070 });
11071 }
11072 }
11073 return try_evaluate(compressor, self);
11074
11075 function in_try(level, node) {
11076 var right = self.right;
11077 self.right = make_node(AST_Null, right);
11078 var may_throw = node.may_throw(compressor);
11079 self.right = right;
11080 var parent;
11081 while (parent = compressor.parent(level++)) {
11082 if (parent === scope) return false;
11083 if (parent instanceof AST_Try) {
11084 if (parent.bfinally) return true;
11085 if (may_throw && parent.bcatch) return true;
11086 }
11087 }
11088 }
11089
11090 function strip_assignment() {
11091 return (self.operator != "=" ? make_node(AST_Binary, self, {
11092 operator: self.operator.slice(0, -1),
11093 left: self.left,
11094 right: self.right
11095 }) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
11096 }
11097 });
11098
11099 OPT(AST_Conditional, function(self, compressor) {
11100 if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
11101 var expressions = self.condition.expressions.slice();
11102 self.condition = expressions.pop();
11103 expressions.push(self);
11104 return make_sequence(self, expressions);
11105 }
11106 if (!compressor.option("conditionals")) return self;
11107 var condition = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
11108 if (!condition) {
11109 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
11110 return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
11111 } else if (!(condition instanceof AST_Node)) {
11112 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
11113 return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
11114 }
11115 var negated = condition.negate(compressor, first_in_statement(compressor));
11116 if (best_of(compressor, condition, negated) === negated) {
11117 self = make_node(AST_Conditional, self, {
11118 condition: negated,
11119 consequent: self.alternative,
11120 alternative: self.consequent
11121 });
11122 negated = condition;
11123 condition = self.condition;
11124 }
11125 var consequent = self.consequent;
11126 var alternative = self.alternative;
11127 if (repeatable(compressor, condition)) {
11128 // x ? x : y ---> x || y
11129 if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
11130 operator: "||",
11131 left: condition,
11132 right: alternative,
11133 }).optimize(compressor);
11134 // x ? y : x ---> x && y
11135 if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
11136 operator: "&&",
11137 left: condition,
11138 right: consequent,
11139 }).optimize(compressor);
11140 }
11141 // if (foo) exp = something; else exp = something_else;
11142 // |
11143 // v
11144 // exp = foo ? something : something_else;
11145 var seq_tail = consequent.tail_node();
11146 if (seq_tail instanceof AST_Assign) {
11147 var is_eq = seq_tail.operator == "=";
11148 var alt_tail = is_eq ? alternative.tail_node() : alternative;
11149 if ((is_eq || consequent === seq_tail)
11150 && alt_tail instanceof AST_Assign
11151 && seq_tail.operator == alt_tail.operator
11152 && seq_tail.left.equivalent_to(alt_tail.left)
11153 && (is_eq && seq_tail.left instanceof AST_SymbolRef
11154 || !condition.has_side_effects(compressor)
11155 && can_shift_lhs_of_tail(consequent)
11156 && can_shift_lhs_of_tail(alternative))) {
11157 return make_node(AST_Assign, self, {
11158 operator: seq_tail.operator,
11159 left: seq_tail.left,
11160 right: make_node(AST_Conditional, self, {
11161 condition: condition,
11162 consequent: pop_lhs(consequent),
11163 alternative: pop_lhs(alternative)
11164 })
11165 });
11166 }
11167 }
11168 // x ? y : y ---> x, y
11169 if (consequent.equivalent_to(alternative)) return make_sequence(self, [
11170 condition,
11171 consequent
11172 ]).optimize(compressor);
11173 // x ? y.p : z.p ---> (x ? y : z).p
11174 // x ? y(a) : z(a) ---> (x ? y : z)(a)
11175 // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
11176 var combined = combine_tail(consequent, alternative, true);
11177 if (combined) return combined;
11178 // x ? y(a) : y(b) ---> y(x ? a : b)
11179 var arg_index;
11180 if (consequent instanceof AST_Call
11181 && alternative.TYPE == consequent.TYPE
11182 && (arg_index = arg_diff(consequent, alternative)) >= 0
11183 && consequent.expression.equivalent_to(alternative.expression)
11184 && !condition.has_side_effects(compressor)
11185 && !consequent.expression.has_side_effects(compressor)) {
11186 var node = consequent.clone();
11187 var arg = consequent.args[arg_index];
11188 node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, {
11189 expression: make_node(AST_Conditional, self, {
11190 condition: condition,
11191 consequent: arg.expression,
11192 alternative: alternative.args[arg_index].expression,
11193 }),
11194 }) : make_node(AST_Conditional, self, {
11195 condition: condition,
11196 consequent: arg,
11197 alternative: alternative.args[arg_index],
11198 });
11199 return node;
11200 }
11201 // x ? (y ? a : b) : b ---> x && y ? a : b
11202 if (consequent instanceof AST_Conditional
11203 && consequent.alternative.equivalent_to(alternative)) {
11204 return make_node(AST_Conditional, self, {
11205 condition: make_node(AST_Binary, self, {
11206 left: condition,
11207 operator: "&&",
11208 right: consequent.condition
11209 }),
11210 consequent: consequent.consequent,
11211 alternative: alternative
11212 });
11213 }
11214 // x ? (y ? a : b) : a ---> !x || y ? a : b
11215 if (consequent instanceof AST_Conditional
11216 && consequent.consequent.equivalent_to(alternative)) {
11217 return make_node(AST_Conditional, self, {
11218 condition: make_node(AST_Binary, self, {
11219 left: negated,
11220 operator: "||",
11221 right: consequent.condition
11222 }),
11223 consequent: alternative,
11224 alternative: consequent.alternative
11225 });
11226 }
11227 // x ? a : (y ? a : b) ---> x || y ? a : b
11228 if (alternative instanceof AST_Conditional
11229 && consequent.equivalent_to(alternative.consequent)) {
11230 return make_node(AST_Conditional, self, {
11231 condition: make_node(AST_Binary, self, {
11232 left: condition,
11233 operator: "||",
11234 right: alternative.condition
11235 }),
11236 consequent: consequent,
11237 alternative: alternative.alternative
11238 });
11239 }
11240 // x ? b : (y ? a : b) ---> !x && y ? a : b
11241 if (alternative instanceof AST_Conditional
11242 && consequent.equivalent_to(alternative.alternative)) {
11243 return make_node(AST_Conditional, self, {
11244 condition: make_node(AST_Binary, self, {
11245 left: negated,
11246 operator: "&&",
11247 right: alternative.condition
11248 }),
11249 consequent: alternative.consequent,
11250 alternative: consequent
11251 });
11252 }
11253 // x ? (a, c) : (b, c) ---> x ? a : b, c
11254 if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
11255 && consequent.tail_node().equivalent_to(alternative.tail_node())) {
11256 return make_sequence(self, [
11257 make_node(AST_Conditional, self, {
11258 condition: condition,
11259 consequent: pop_seq(consequent),
11260 alternative: pop_seq(alternative)
11261 }),
11262 consequent.tail_node()
11263 ]).optimize(compressor);
11264 }
11265 // x ? y && a : a ---> (!x || y) && a
11266 if (consequent instanceof AST_Binary
11267 && consequent.operator == "&&"
11268 && consequent.right.equivalent_to(alternative)) {
11269 return make_node(AST_Binary, self, {
11270 operator: "&&",
11271 left: make_node(AST_Binary, self, {
11272 operator: "||",
11273 left: negated,
11274 right: consequent.left
11275 }),
11276 right: alternative
11277 }).optimize(compressor);
11278 }
11279 // x ? y || a : a ---> x && y || a
11280 if (consequent instanceof AST_Binary
11281 && consequent.operator == "||"
11282 && consequent.right.equivalent_to(alternative)) {
11283 return make_node(AST_Binary, self, {
11284 operator: "||",
11285 left: make_node(AST_Binary, self, {
11286 operator: "&&",
11287 left: condition,
11288 right: consequent.left
11289 }),
11290 right: alternative
11291 }).optimize(compressor);
11292 }
11293 // x ? a : y && a ---> (x || y) && a
11294 if (alternative instanceof AST_Binary
11295 && alternative.operator == "&&"
11296 && alternative.right.equivalent_to(consequent)) {
11297 return make_node(AST_Binary, self, {
11298 operator: "&&",
11299 left: make_node(AST_Binary, self, {
11300 operator: "||",
11301 left: condition,
11302 right: alternative.left
11303 }),
11304 right: consequent
11305 }).optimize(compressor);
11306 }
11307 // x ? a : y || a ---> !x && y || a
11308 if (alternative instanceof AST_Binary
11309 && alternative.operator == "||"
11310 && alternative.right.equivalent_to(consequent)) {
11311 return make_node(AST_Binary, self, {
11312 operator: "||",
11313 left: make_node(AST_Binary, self, {
11314 operator: "&&",
11315 left: negated,
11316 right: alternative.left
11317 }),
11318 right: consequent
11319 }).optimize(compressor);
11320 }
11321 var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
11322 if (is_true(consequent)) {
11323 if (is_false(alternative)) {
11324 // c ? true : false ---> !!c
11325 return booleanize(condition);
11326 }
11327 // c ? true : x ---> !!c || x
11328 return make_node(AST_Binary, self, {
11329 operator: "||",
11330 left: booleanize(condition),
11331 right: alternative
11332 });
11333 }
11334 if (is_false(consequent)) {
11335 if (is_true(alternative)) {
11336 // c ? false : true ---> !c
11337 return booleanize(condition.negate(compressor));
11338 }
11339 // c ? false : x ---> !c && x
11340 return make_node(AST_Binary, self, {
11341 operator: "&&",
11342 left: booleanize(condition.negate(compressor)),
11343 right: alternative
11344 });
11345 }
11346 if (is_true(alternative)) {
11347 // c ? x : true ---> !c || x
11348 return make_node(AST_Binary, self, {
11349 operator: "||",
11350 left: booleanize(condition.negate(compressor)),
11351 right: consequent
11352 });
11353 }
11354 if (is_false(alternative)) {
11355 // c ? x : false ---> !!c && x
11356 return make_node(AST_Binary, self, {
11357 operator: "&&",
11358 left: booleanize(condition),
11359 right: consequent
11360 });
11361 }
11362 if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
11363 return self;
11364
11365 function booleanize(node) {
11366 if (node.is_boolean(compressor)) return node;
11367 // !!expression
11368 return make_node(AST_UnaryPrefix, node, {
11369 operator: "!",
11370 expression: node.negate(compressor)
11371 });
11372 }
11373
11374 // AST_True or !0
11375 function is_true(node) {
11376 return node instanceof AST_True
11377 || in_bool
11378 && node instanceof AST_Constant
11379 && node.value
11380 || (node instanceof AST_UnaryPrefix
11381 && node.operator == "!"
11382 && node.expression instanceof AST_Constant
11383 && !node.expression.value);
11384 }
11385 // AST_False or !1 or void 0
11386 function is_false(node) {
11387 return node instanceof AST_False
11388 || in_bool
11389 && (node instanceof AST_Constant
11390 && !node.value
11391 || node instanceof AST_UnaryPrefix
11392 && node.operator == "void"
11393 && !node.expression.has_side_effects(compressor))
11394 || (node instanceof AST_UnaryPrefix
11395 && node.operator == "!"
11396 && node.expression instanceof AST_Constant
11397 && node.expression.value);
11398 }
11399
11400 function arg_diff(consequent, alternative) {
11401 var a = consequent.args;
11402 var b = alternative.args;
11403 var len = a.length;
11404 if (len != b.length) return -2;
11405 for (var i = 0; i < len; i++) {
11406 if (!a[i].equivalent_to(b[i])) {
11407 if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
11408 for (var j = i + 1; j < len; j++) {
11409 if (!a[j].equivalent_to(b[j])) return -2;
11410 }
11411 return i;
11412 }
11413 }
11414 return -1;
11415 }
11416
11417 function is_tail_equivalent(consequent, alternative) {
11418 if (consequent.TYPE != alternative.TYPE) return;
11419 if (consequent.optional != alternative.optional) return;
11420 if (consequent instanceof AST_Call) {
11421 if (arg_diff(consequent, alternative) != -1) return;
11422 return consequent.TYPE != "Call"
11423 || !(consequent.expression instanceof AST_PropAccess
11424 || alternative.expression instanceof AST_PropAccess)
11425 || is_tail_equivalent(consequent.expression, alternative.expression);
11426 }
11427 if (!(consequent instanceof AST_PropAccess)) return;
11428 var p = consequent.property;
11429 var q = alternative.property;
11430 return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
11431 && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
11432 }
11433
11434 function combine_tail(consequent, alternative, top) {
11435 if (!is_tail_equivalent(consequent, alternative)) return !top && make_node(AST_Conditional, self, {
11436 condition: condition,
11437 consequent: consequent,
11438 alternative: alternative,
11439 });
11440 var node = consequent.clone();
11441 node.expression = combine_tail(consequent.expression, alternative.expression);
11442 return node;
11443 }
11444
11445 function can_shift_lhs_of_tail(node) {
11446 return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
11447 return !expr.has_side_effects(compressor);
11448 });
11449 }
11450
11451 function pop_lhs(node) {
11452 if (!(node instanceof AST_Sequence)) return node.right;
11453 var exprs = node.expressions.slice();
11454 exprs.push(exprs.pop().right);
11455 return make_sequence(node, exprs);
11456 }
11457
11458 function pop_seq(node) {
11459 if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
11460 value: 0
11461 });
11462 return make_sequence(node, node.expressions.slice(0, -1));
11463 }
11464 });
11465
11466 OPT(AST_Boolean, function(self, compressor) {
11467 if (!compressor.option("booleans")) return self;
11468 if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
11469 value: +self.value
11470 });
11471 var p = compressor.parent();
11472 if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
11473 AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
11474 operator : p.operator,
11475 value : self.value,
11476 file : p.start.file,
11477 line : p.start.line,
11478 col : p.start.col,
11479 });
11480 return make_node(AST_Number, self, {
11481 value: +self.value
11482 });
11483 }
11484 return make_node(AST_UnaryPrefix, self, {
11485 operator: "!",
11486 expression: make_node(AST_Number, self, {
11487 value: 1 - self.value
11488 })
11489 });
11490 });
11491
11492 OPT(AST_Spread, function(self, compressor) {
11493 var exp = self.expression;
11494 if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
11495 return List.splice(exp.elements.map(function(node) {
11496 return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
11497 }));
11498 }
11499 return self;
11500 });
11501
11502 function safe_to_flatten(value, compressor) {
11503 if (value instanceof AST_SymbolRef) {
11504 value = value.fixed_value();
11505 }
11506 if (!value) return false;
11507 if (!(value instanceof AST_Lambda)) return true;
11508 var parent = compressor.parent();
11509 if (parent.TYPE != "Call") return true;
11510 if (parent.expression !== compressor.self()) return true;
11511 return !value.contains_this();
11512 }
11513
11514 OPT(AST_Sub, function(self, compressor) {
11515 var expr = self.expression;
11516 var prop = self.property;
11517 var terminated = trim_optional_chain(self, compressor);
11518 if (terminated) return terminated;
11519 if (compressor.option("properties")) {
11520 var key = prop.evaluate(compressor);
11521 if (key !== prop) {
11522 if (typeof key == "string") {
11523 if (key == "undefined") {
11524 key = undefined;
11525 } else {
11526 var value = parseFloat(key);
11527 if (value.toString() == key) {
11528 key = value;
11529 }
11530 }
11531 }
11532 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
11533 var property = "" + key;
11534 if (is_identifier_string(property)
11535 && property.length <= prop.print_to_string().length + 1) {
11536 return make_node(AST_Dot, self, {
11537 optional: self.optional,
11538 expression: expr,
11539 property: property,
11540 }).optimize(compressor);
11541 }
11542 }
11543 }
11544 var parent = compressor.parent();
11545 var assigned = is_lhs(compressor.self(), parent);
11546 var def, fn, fn_parent;
11547 if (compressor.option("arguments")
11548 && expr instanceof AST_SymbolRef
11549 && is_arguments(def = expr.definition())
11550 && !expr.in_arg
11551 && prop instanceof AST_Number
11552 && (fn = def.scope) === find_lambda()
11553 && fn.uses_arguments < (assigned ? 2 : 3)) {
11554 var index = prop.value;
11555 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
11556 if (!def.deleted) def.deleted = [];
11557 def.deleted[index] = true;
11558 }
11559 var argname = fn.argnames[index];
11560 if (def.deleted && def.deleted[index]) {
11561 argname = null;
11562 } else if (argname) {
11563 var arg_def;
11564 if (!(argname instanceof AST_SymbolFunarg)
11565 || argname.name == "await"
11566 || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) {
11567 argname = null;
11568 } else if (compressor.has_directive("use strict")
11569 || fn.name
11570 || fn.rest
11571 || !(fn_parent instanceof AST_Call && index < fn_parent.args.length)
11572 || !all(fn.argnames, function(argname) {
11573 return argname instanceof AST_SymbolFunarg;
11574 })) {
11575 if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
11576 }
11577 } else if ((assigned || !has_reassigned())
11578 && index < fn.argnames.length + 5
11579 && compressor.drop_fargs(fn, fn_parent)
11580 && !fn.rest) {
11581 while (index >= fn.argnames.length) {
11582 argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
11583 fn.argnames.push(argname);
11584 }
11585 }
11586 if (argname && find_if(function(node) {
11587 return node.name === argname.name;
11588 }, fn.argnames) === argname) {
11589 if (assigned) def.reassigned--;
11590 var sym = make_node(AST_SymbolRef, self, argname);
11591 sym.reference();
11592 delete argname.__unused;
11593 return sym;
11594 }
11595 }
11596 if (assigned) return self;
11597 if (compressor.option("sequences")
11598 && parent.TYPE != "Call"
11599 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
11600 var seq = lift_sequence_in_expression(self, compressor);
11601 if (seq !== self) return seq.optimize(compressor);
11602 }
11603 if (key !== prop) {
11604 var sub = self.flatten_object(property, compressor);
11605 if (sub) {
11606 expr = self.expression = sub.expression;
11607 prop = self.property = sub.property;
11608 }
11609 }
11610 var elements;
11611 if (compressor.option("properties")
11612 && compressor.option("side_effects")
11613 && prop instanceof AST_Number
11614 && expr instanceof AST_Array
11615 && all(elements = expr.elements, function(value) {
11616 return !(value instanceof AST_Spread);
11617 })) {
11618 var index = prop.value;
11619 var retValue = elements[index];
11620 if (safe_to_flatten(retValue, compressor)) {
11621 var is_hole = retValue instanceof AST_Hole;
11622 var flatten = !is_hole;
11623 var values = [];
11624 for (var i = elements.length; --i > index;) {
11625 var value = elements[i].drop_side_effect_free(compressor);
11626 if (value) {
11627 values.unshift(value);
11628 if (flatten && value.has_side_effects(compressor)) flatten = false;
11629 }
11630 }
11631 if (!flatten) values.unshift(retValue);
11632 while (--i >= 0) {
11633 var value = elements[i].drop_side_effect_free(compressor);
11634 if (value) {
11635 values.unshift(value);
11636 } else if (is_hole) {
11637 values.unshift(make_node(AST_Hole, elements[i]));
11638 } else {
11639 index--;
11640 }
11641 }
11642 if (flatten) {
11643 values.push(retValue);
11644 return make_sequence(self, values).optimize(compressor);
11645 } else return make_node(AST_Sub, self, {
11646 expression: make_node(AST_Array, expr, { elements: values }),
11647 property: make_node(AST_Number, prop, { value: index }),
11648 });
11649 }
11650 }
11651 return try_evaluate(compressor, self);
11652
11653 function find_lambda() {
11654 var i = 0, p;
11655 while (p = compressor.parent(i++)) {
11656 if (p instanceof AST_Lambda) {
11657 if (p instanceof AST_Accessor) return;
11658 if (is_arrow(p)) continue;
11659 fn_parent = compressor.parent(i);
11660 return p;
11661 }
11662 }
11663 }
11664
11665 function has_reassigned() {
11666 return !compressor.option("reduce_vars") || def.reassigned;
11667 }
11668 });
11669
11670 AST_Arrow.DEFMETHOD("contains_super", return_false);
11671 AST_AsyncArrow.DEFMETHOD("contains_super", return_false);
11672 AST_Lambda.DEFMETHOD("contains_super", function() {
11673 var result;
11674 var self = this;
11675 self.walk(new TreeWalker(function(node) {
11676 if (result) return true;
11677 if (node instanceof AST_Super) return result = true;
11678 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
11679 }));
11680 return result;
11681 });
11682 AST_LambdaDefinition.DEFMETHOD("contains_super", return_false);
11683 AST_Scope.DEFMETHOD("contains_super", return_false);
11684
11685 AST_Arrow.DEFMETHOD("contains_this", return_false);
11686 AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
11687 AST_Node.DEFMETHOD("contains_this", function() {
11688 var result;
11689 var self = this;
11690 self.walk(new TreeWalker(function(node) {
11691 if (result) return true;
11692 if (node instanceof AST_This) return result = true;
11693 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
11694 }));
11695 return result;
11696 });
11697
11698 function can_hoist_property(prop) {
11699 return prop instanceof AST_ObjectKeyVal
11700 && typeof prop.key == "string"
11701 && !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
11702 }
11703
11704 AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
11705 if (!compressor.option("properties")) return;
11706 if (key === "__proto__") return;
11707 var expr = this.expression;
11708 if (expr instanceof AST_Object) {
11709 var props = expr.properties;
11710 for (var i = props.length; --i >= 0;) {
11711 var prop = props[i];
11712 if (prop.key != key) continue;
11713 if (!all(props, can_hoist_property)) break;
11714 if (!safe_to_flatten(prop.value, compressor)) break;
11715 props = props.map(function(prop) {
11716 return prop.value;
11717 });
11718 if (prop instanceof AST_ObjectMethod
11719 && prop.value instanceof AST_Function
11720 && !(compressor.parent() instanceof AST_Call)) {
11721 if (prop.value.uses_arguments) break;
11722 props[i] = make_node(AST_Arrow, prop.value, prop.value);
11723 }
11724 return make_node(AST_Sub, this, {
11725 expression: make_node(AST_Array, expr, { elements: props }),
11726 property: make_node(AST_Number, this, { value: i }),
11727 });
11728 }
11729 }
11730 });
11731
11732 OPT(AST_Dot, function(self, compressor) {
11733 if (self.property == "arguments" || self.property == "caller") {
11734 AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
11735 prop: self.property,
11736 file: self.start.file,
11737 line: self.start.line,
11738 col: self.start.col,
11739 });
11740 }
11741 var parent = compressor.parent();
11742 if (is_lhs(compressor.self(), parent)) return self;
11743 var terminated = trim_optional_chain(self, compressor);
11744 if (terminated) return terminated;
11745 if (compressor.option("sequences")
11746 && parent.TYPE != "Call"
11747 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
11748 var seq = lift_sequence_in_expression(self, compressor);
11749 if (seq !== self) return seq.optimize(compressor);
11750 }
11751 if (compressor.option("unsafe_proto")
11752 && self.expression instanceof AST_Dot
11753 && self.expression.property == "prototype") {
11754 var exp = self.expression.expression;
11755 if (is_undeclared_ref(exp)) switch (exp.name) {
11756 case "Array":
11757 self.expression = make_node(AST_Array, self.expression, {
11758 elements: []
11759 });
11760 break;
11761 case "Function":
11762 self.expression = make_node(AST_Function, self.expression, {
11763 argnames: [],
11764 body: []
11765 }).init_vars(exp.scope);
11766 break;
11767 case "Number":
11768 self.expression = make_node(AST_Number, self.expression, {
11769 value: 0
11770 });
11771 break;
11772 case "Object":
11773 self.expression = make_node(AST_Object, self.expression, {
11774 properties: []
11775 });
11776 break;
11777 case "RegExp":
11778 self.expression = make_node(AST_RegExp, self.expression, {
11779 value: /t/
11780 });
11781 break;
11782 case "String":
11783 self.expression = make_node(AST_String, self.expression, {
11784 value: ""
11785 });
11786 break;
11787 }
11788 }
11789 var sub = self.flatten_object(self.property, compressor);
11790 if (sub) return sub.optimize(compressor);
11791 return try_evaluate(compressor, self);
11792 });
11793
11794 OPT(AST_DestructuredArray, function(self, compressor) {
11795 if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) {
11796 self.elements = self.elements.concat(self.rest.elements);
11797 self.rest = null;
11798 }
11799 return self;
11800 });
11801
11802 OPT(AST_DestructuredKeyVal, function(self, compressor) {
11803 if (compressor.option("objects")) {
11804 var key = self.key;
11805 if (key instanceof AST_Node) {
11806 key = key.evaluate(compressor);
11807 if (key !== self.key) self.key = "" + key;
11808 }
11809 }
11810 return self;
11811 });
11812
11813 OPT(AST_Object, function(self, compressor) {
11814 if (!compressor.option("objects")) return self;
11815 var changed = false;
11816 var found = false;
11817 var generated = false;
11818 var keep_duplicate = compressor.has_directive("use strict");
11819 var keys = new Dictionary();
11820 var values = [];
11821 self.properties.forEach(function(prop) {
11822 if (!(prop instanceof AST_Spread)) return process(prop);
11823 found = true;
11824 var exp = prop.expression;
11825 if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
11826 if (prop instanceof AST_ObjectGetter) return false;
11827 if (prop instanceof AST_Spread) return false;
11828 if (prop.key !== "__proto__") return true;
11829 if (prop instanceof AST_ObjectSetter) return true;
11830 return !prop.value.has_side_effects(compressor);
11831 })) {
11832 changed = true;
11833 exp.properties.forEach(function(prop) {
11834 var key = prop.key;
11835 var setter = prop instanceof AST_ObjectSetter;
11836 if (key === "__proto__") {
11837 if (!setter) return;
11838 key = make_node_from_constant(key, prop);
11839 }
11840 process(setter ? make_node(AST_ObjectKeyVal, prop, {
11841 key: key,
11842 value: make_node(AST_Undefined, prop).optimize(compressor),
11843 }) : prop);
11844 });
11845 } else {
11846 generated = true;
11847 flush();
11848 values.push(prop);
11849 }
11850 });
11851 flush();
11852 if (!changed) return self;
11853 if (found && generated && values.length == 1) {
11854 var value = values[0];
11855 if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
11856 value.key = "" + value.key.value;
11857 }
11858 }
11859 return make_node(AST_Object, self, { properties: values });
11860
11861 function flush() {
11862 keys.each(function(props) {
11863 if (props.length == 1) return values.push(props[0]);
11864 changed = true;
11865 var tail = keep_duplicate && !generated && props.pop();
11866 values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
11867 key: props[0].key,
11868 value: make_sequence(self, props.map(function(prop) {
11869 return prop.value;
11870 }))
11871 }));
11872 if (tail) values.push(tail);
11873 });
11874 keys = new Dictionary();
11875 }
11876
11877 function process(prop) {
11878 var key = prop.key;
11879 if (key instanceof AST_Node) {
11880 found = true;
11881 key = key.evaluate(compressor);
11882 if (key === prop.key || key === "__proto__") {
11883 generated = true;
11884 } else {
11885 key = prop.key = "" + key;
11886 }
11887 }
11888 if (can_hoist_property(prop)) {
11889 if (prop.value.has_side_effects(compressor)) flush();
11890 keys.add(key, prop);
11891 } else {
11892 flush();
11893 values.push(prop);
11894 }
11895 if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
11896 generated = true;
11897 if (keys.has(key)) prop = keys.get(key)[0];
11898 prop.key = make_node(AST_Number, prop, { value: +key });
11899 }
11900 }
11901 });
11902
11903 OPT(AST_Return, function(self, compressor) {
11904 if (compressor.option("side_effects")
11905 && self.value
11906 && is_undefined(self.value, compressor)
11907 && !in_async_generator(compressor.find_parent(AST_Scope))) {
11908 self.value = null;
11909 }
11910 return self;
11911 });
11912})(function(node, optimizer) {
11913 node.DEFMETHOD("optimize", function(compressor) {
11914 var self = this;
11915 if (self._optimized) return self;
11916 if (compressor.has_directive("use asm")) return self;
11917 var opt = optimizer(self, compressor);
11918 opt._optimized = true;
11919 return opt;
11920 });
11921});