UNPKG

348 kBJavaScriptView Raw
1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
5
6 -------------------------------- (C) ---------------------------------
7
8 Author: Mihai Bazon
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
11
12 Distributed under the BSD license:
13
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
18 are met:
19
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
22 disclaimer.
23
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
28
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 SUCH DAMAGE.
41
42 ***********************************************************************/
43
44"use strict";
45
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 arguments : !false_by_default,
52 assignments : !false_by_default,
53 booleans : !false_by_default,
54 collapse_vars : !false_by_default,
55 comparisons : !false_by_default,
56 conditionals : !false_by_default,
57 dead_code : !false_by_default,
58 directives : !false_by_default,
59 drop_console : false,
60 drop_debugger : !false_by_default,
61 evaluate : !false_by_default,
62 expression : false,
63 functions : !false_by_default,
64 global_defs : false,
65 hoist_funs : false,
66 hoist_props : !false_by_default,
67 hoist_vars : false,
68 ie8 : false,
69 if_return : !false_by_default,
70 inline : !false_by_default,
71 join_vars : !false_by_default,
72 keep_fargs : false_by_default || "strict",
73 keep_fnames : false,
74 keep_infinity : false,
75 loops : !false_by_default,
76 negate_iife : !false_by_default,
77 objects : !false_by_default,
78 passes : 1,
79 properties : !false_by_default,
80 pure_getters : !false_by_default && "strict",
81 pure_funcs : null,
82 reduce_funcs : !false_by_default,
83 reduce_vars : !false_by_default,
84 sequences : !false_by_default,
85 side_effects : !false_by_default,
86 strings : !false_by_default,
87 switches : !false_by_default,
88 top_retain : null,
89 toplevel : !!(options && options["top_retain"]),
90 typeofs : !false_by_default,
91 unsafe : false,
92 unsafe_comps : false,
93 unsafe_Function : false,
94 unsafe_math : false,
95 unsafe_proto : false,
96 unsafe_regexp : false,
97 unsafe_undefined: false,
98 unused : !false_by_default,
99 }, true);
100 var evaluate = this.options["evaluate"];
101 this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
102 var global_defs = this.options["global_defs"];
103 if (typeof global_defs == "object") for (var key in global_defs) {
104 if (/^@/.test(key) && HOP(global_defs, key)) {
105 global_defs[key.slice(1)] = parse(global_defs[key], {
106 expression: true
107 });
108 }
109 }
110 if (this.options["inline"] === true) this.options["inline"] = 3;
111 var keep_fargs = this.options["keep_fargs"];
112 this.drop_fargs = keep_fargs == "strict" ? function(lambda, parent) {
113 if (lambda.length_read) return false;
114 var name = lambda.name;
115 if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
116 if (name.fixed_value() !== lambda) return false;
117 var def = name.definition();
118 if (def.direct_access) return false;
119 var escaped = def.escaped;
120 return escaped && escaped.depth != 1;
121 } : keep_fargs ? return_false : return_true;
122 var pure_funcs = this.options["pure_funcs"];
123 if (typeof pure_funcs == "function") {
124 this.pure_funcs = pure_funcs;
125 } else if (typeof pure_funcs == "string") {
126 this.pure_funcs = function(node) {
127 return pure_funcs !== node.expression.print_to_string();
128 };
129 } else if (Array.isArray(pure_funcs)) {
130 this.pure_funcs = function(node) {
131 return !member(node.expression.print_to_string(), pure_funcs);
132 };
133 } else {
134 this.pure_funcs = return_true;
135 }
136 var sequences = this.options["sequences"];
137 this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
138 var top_retain = this.options["top_retain"];
139 if (top_retain instanceof RegExp) {
140 this.top_retain = function(def) {
141 return top_retain.test(def.name);
142 };
143 } else if (typeof top_retain == "function") {
144 this.top_retain = top_retain;
145 } else if (top_retain) {
146 if (typeof top_retain == "string") {
147 top_retain = top_retain.split(/,/);
148 }
149 this.top_retain = function(def) {
150 return member(def.name, top_retain);
151 };
152 }
153 var toplevel = this.options["toplevel"];
154 this.toplevel = typeof toplevel == "string" ? {
155 funcs: /funcs/.test(toplevel),
156 vars: /vars/.test(toplevel)
157 } : {
158 funcs: toplevel,
159 vars: toplevel
160 };
161}
162
163Compressor.prototype = new TreeTransformer;
164merge(Compressor.prototype, {
165 option: function(key) { return this.options[key] },
166 exposed: function(def) {
167 if (def.global) for (var i = 0; i < def.orig.length; i++)
168 if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
169 return true;
170 return false;
171 },
172 compress: function(node) {
173 node = node.resolve_defines(this);
174 if (this.option("expression")) {
175 node.process_expression(true);
176 }
177 var passes = +this.options.passes || 1;
178 var min_count = 1 / 0;
179 var stopping = false;
180 var mangle = { ie8: this.option("ie8") };
181 for (var pass = 0; pass < passes; pass++) {
182 node.figure_out_scope(mangle);
183 if (pass > 0 || this.option("reduce_vars"))
184 node.reset_opt_flags(this);
185 node = node.transform(this);
186 if (passes > 1) {
187 var count = 0;
188 node.walk(new TreeWalker(function() {
189 count++;
190 }));
191 AST_Node.info("pass " + pass + ": last_count: " + min_count + ", count: " + count);
192 if (count < min_count) {
193 min_count = count;
194 stopping = false;
195 } else if (stopping) {
196 break;
197 } else {
198 stopping = true;
199 }
200 }
201 }
202 if (this.option("expression")) {
203 node.process_expression(false);
204 }
205 return node;
206 },
207 before: function(node, descend, in_list) {
208 if (node._squeezed) return node;
209 var is_scope = node instanceof AST_Scope;
210 if (is_scope) {
211 node.hoist_properties(this);
212 node.hoist_declarations(this);
213 node.process_boolean_returns(this);
214 }
215 // Before https://github.com/mishoo/UglifyJS2/pull/1602 AST_Node.optimize()
216 // would call AST_Node.transform() if a different instance of AST_Node is
217 // produced after OPT().
218 // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
219 // Migrate and defer all children's AST_Node.transform() to below, which
220 // will now happen after this parent AST_Node has been properly substituted
221 // thus gives a consistent AST snapshot.
222 descend(node, this);
223 // Existing code relies on how AST_Node.optimize() worked, and omitting the
224 // following replacement call would result in degraded efficiency of both
225 // output and performance.
226 descend(node, this);
227 var opt = node.optimize(this);
228 if (is_scope) {
229 opt.drop_unused(this);
230 descend(opt, this);
231 }
232 if (opt === node) opt._squeezed = true;
233 return opt;
234 }
235});
236
237(function(OPT) {
238 OPT(AST_Node, function(self, compressor) {
239 return self;
240 });
241
242 AST_Node.DEFMETHOD("equivalent_to", function(node) {
243 return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
244 });
245
246 AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
247 var self = this;
248 var tt = new TreeTransformer(function(node) {
249 if (insert && node instanceof AST_SimpleStatement) {
250 return make_node(AST_Return, node, {
251 value: node.body
252 });
253 }
254 if (!insert && node instanceof AST_Return) {
255 return transform ? transform(node) : make_node(AST_SimpleStatement, node, {
256 body: node.value || make_node(AST_UnaryPrefix, node, {
257 operator: "void",
258 expression: make_node(AST_Number, node, {
259 value: 0
260 })
261 })
262 });
263 }
264 if (node instanceof AST_Lambda && node !== self) {
265 return node;
266 }
267 if (node instanceof AST_Block) {
268 var index = node.body.length - 1;
269 if (index >= 0) {
270 node.body[index] = node.body[index].transform(tt);
271 }
272 } else if (node instanceof AST_If) {
273 node.body = node.body.transform(tt);
274 if (node.alternative) {
275 node.alternative = node.alternative.transform(tt);
276 }
277 } else if (node instanceof AST_With) {
278 node.body = node.body.transform(tt);
279 }
280 return node;
281 });
282 self.transform(tt);
283 });
284
285 function read_property(obj, node) {
286 var key = node.getProperty();
287 if (key instanceof AST_Node) return;
288 var value;
289 if (obj instanceof AST_Array) {
290 var elements = obj.elements;
291 if (key == "length") return make_node_from_constant(elements.length, obj);
292 if (typeof key == "number" && key in elements) value = elements[key];
293 } else if (obj instanceof AST_Lambda) {
294 if (key == "length") {
295 obj.length_read = true;
296 return make_node_from_constant(obj.argnames.length, obj);
297 }
298 } else if (obj instanceof AST_Object) {
299 key = "" + key;
300 var props = obj.properties;
301 for (var i = props.length; --i >= 0;) {
302 var prop = props[i];
303 if (!(prop instanceof AST_ObjectKeyVal)) return;
304 if (!value && props[i].key === key) value = props[i].value;
305 }
306 }
307 return value instanceof AST_SymbolRef && value.fixed_value() || value;
308 }
309
310 function is_read_only_fn(value, name) {
311 if (value instanceof AST_Boolean) return native_fns.Boolean[name];
312 if (value instanceof AST_Number) return native_fns.Number[name];
313 if (value instanceof AST_String) return native_fns.String[name];
314 if (name == "valueOf") return false;
315 if (value instanceof AST_Array) return native_fns.Array[name];
316 if (value instanceof AST_Function) return native_fns.Function[name];
317 if (value instanceof AST_Object) return native_fns.Object[name];
318 if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
319 }
320
321 function is_modified(compressor, tw, node, value, level, immutable, recursive) {
322 var parent = tw.parent(level);
323 if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
324 return;
325 }
326 var lhs = is_lhs(node, parent);
327 if (lhs) return lhs;
328 if (!immutable
329 && parent instanceof AST_Call
330 && parent.expression === node
331 && !parent.is_expr_pure(compressor)
332 && (!(value instanceof AST_Function)
333 || !(parent instanceof AST_New) && value.contains_this())) {
334 return true;
335 }
336 if (parent instanceof AST_Array) {
337 return is_modified(compressor, tw, parent, parent, level + 1);
338 }
339 if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
340 var obj = tw.parent(level + 1);
341 return is_modified(compressor, tw, obj, obj, level + 2);
342 }
343 if (parent instanceof AST_PropAccess && parent.expression === node) {
344 var prop = read_property(value, parent);
345 return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
346 }
347 }
348
349 function is_arguments(def) {
350 if (def.name != "arguments") return false;
351 var orig = def.orig;
352 return orig.length == 1 && orig[0] instanceof AST_SymbolFunarg;
353 }
354
355 (function(def) {
356 def(AST_Node, noop);
357
358 function reset_def(tw, compressor, def) {
359 def.assignments = 0;
360 def.bool_fn = 0;
361 def.chained = false;
362 def.cross_loop = false;
363 def.direct_access = false;
364 def.escaped = [];
365 def.fixed = !def.scope.pinned()
366 && !compressor.exposed(def)
367 && !(def.init instanceof AST_Function && def.init !== def.scope)
368 && def.init;
369 if (def.fixed instanceof AST_Defun && !all(def.references, function(ref) {
370 var scope = ref.scope;
371 do {
372 if (def.scope === scope) return true;
373 } while (scope instanceof AST_Function && (scope = scope.parent_scope));
374 })) {
375 tw.defun_ids[def.id] = false;
376 }
377 def.recursive_refs = 0;
378 def.references = [];
379 def.should_replace = undefined;
380 def.single_use = undefined;
381 }
382
383 function reset_variables(tw, compressor, scope) {
384 scope.variables.each(function(def) {
385 reset_def(tw, compressor, def);
386 if (def.fixed === null) {
387 def.safe_ids = tw.safe_ids;
388 mark(tw, def, true);
389 } else if (def.fixed) {
390 tw.loop_ids[def.id] = tw.in_loop;
391 mark(tw, def, true);
392 }
393 });
394 scope.may_call_this = function() {
395 scope.may_call_this = noop;
396 if (!scope.contains_this()) return;
397 scope.functions.each(function(def) {
398 if (def.init instanceof AST_Defun && !(def.id in tw.defun_ids)) {
399 tw.defun_ids[def.id] = false;
400 }
401 });
402 };
403 }
404
405 function mark_defun(tw, def) {
406 if (def.id in tw.defun_ids) {
407 var marker = tw.defun_ids[def.id];
408 if (!marker) return;
409 var visited = tw.defun_visited[def.id];
410 if (marker === tw.safe_ids) {
411 if (!visited) return def.fixed;
412 } else if (visited) {
413 def.init.enclosed.forEach(function(d) {
414 if (def.init.variables.get(d.name) === d) return;
415 if (!safe_to_read(tw, d)) d.fixed = false;
416 });
417 } else {
418 tw.defun_ids[def.id] = false;
419 }
420 } else {
421 if (!tw.in_loop) {
422 tw.defun_ids[def.id] = tw.safe_ids;
423 return def.fixed;
424 }
425 tw.defun_ids[def.id] = false;
426 }
427 }
428
429 function walk_defuns(tw, scope) {
430 scope.functions.each(function(def) {
431 if (def.init instanceof AST_Defun && !tw.defun_visited[def.id]) {
432 tw.defun_ids[def.id] = tw.safe_ids;
433 def.init.walk(tw);
434 }
435 });
436 }
437
438 function push(tw) {
439 tw.safe_ids = Object.create(tw.safe_ids);
440 }
441
442 function pop(tw) {
443 tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
444 }
445
446 function mark(tw, def, safe) {
447 tw.safe_ids[def.id] = safe;
448 }
449
450 function safe_to_read(tw, def) {
451 if (def.single_use == "m") return false;
452 if (tw.safe_ids[def.id]) {
453 if (def.fixed == null) {
454 if (is_arguments(def)) return false;
455 if (def.global && def.name == "arguments") return false;
456 def.fixed = make_node(AST_Undefined, def.orig[0]);
457 }
458 return true;
459 }
460 return def.fixed instanceof AST_Defun;
461 }
462
463 function safe_to_assign(tw, def, scope, value) {
464 if (def.fixed === undefined) return true;
465 if (def.fixed === null && def.safe_ids) {
466 def.safe_ids[def.id] = false;
467 delete def.safe_ids;
468 return true;
469 }
470 if (!HOP(tw.safe_ids, def.id)) return false;
471 if (!safe_to_read(tw, def)) return false;
472 if (def.fixed === false) return false;
473 if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
474 if (def.fixed instanceof AST_Defun) {
475 return value instanceof AST_Node && def.fixed.parent_scope === scope;
476 }
477 return all(def.orig, function(sym) {
478 return !(sym instanceof AST_SymbolDefun || sym instanceof AST_SymbolLambda);
479 });
480 }
481
482 function ref_once(tw, compressor, def) {
483 return compressor.option("unused")
484 && !def.scope.pinned()
485 && def.references.length - def.recursive_refs == 1
486 && tw.loop_ids[def.id] === tw.in_loop;
487 }
488
489 function is_immutable(value) {
490 if (!value) return false;
491 return value.is_constant()
492 || value instanceof AST_Lambda
493 || value instanceof AST_This;
494 }
495
496 function mark_escaped(tw, d, scope, node, value, level, depth) {
497 var parent = tw.parent(level);
498 if (value && value.is_constant()) return;
499 if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
500 || parent instanceof AST_Call && (node !== parent.expression || parent instanceof AST_New)
501 || parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
502 || parent instanceof AST_VarDef && node === parent.value) {
503 d.escaped.push(parent);
504 if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
505 if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
506 return;
507 } else if (parent instanceof AST_Array
508 || parent instanceof AST_Binary && lazy_op[parent.operator]
509 || parent instanceof AST_Conditional && node !== parent.condition
510 || parent instanceof AST_Sequence && node === parent.tail_node()) {
511 mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
512 } else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
513 var obj = tw.parent(level + 1);
514 mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
515 } else if (parent instanceof AST_PropAccess && node === parent.expression) {
516 value = read_property(value, parent);
517 mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
518 if (value) return;
519 }
520 if (level > 0) return;
521 if (parent instanceof AST_Call && node === parent.expression) return;
522 if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
523 if (parent instanceof AST_SimpleStatement) return;
524 if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
525 d.direct_access = true;
526 }
527
528 function mark_assignment_to_arguments(node) {
529 if (!(node instanceof AST_Sub)) return;
530 var expr = node.expression;
531 if (!(expr instanceof AST_SymbolRef)) return;
532 var def = expr.definition();
533 if (is_arguments(def) && node.property instanceof AST_Number) def.reassigned = true;
534 }
535
536 var suppressor = new TreeWalker(function(node) {
537 if (!(node instanceof AST_Symbol)) return;
538 var d = node.definition();
539 if (!d) return;
540 if (node instanceof AST_SymbolRef) d.references.push(node);
541 d.fixed = false;
542 });
543 def(AST_Accessor, function(tw, descend, compressor) {
544 push(tw);
545 reset_variables(tw, compressor, this);
546 descend();
547 pop(tw);
548 walk_defuns(tw, this);
549 return true;
550 });
551 def(AST_Assign, function(tw, descend, compressor) {
552 var node = this;
553 var sym = node.left;
554 if (!(sym instanceof AST_SymbolRef)) {
555 mark_assignment_to_arguments(sym);
556 return;
557 }
558 if (sym.fixed) delete sym.fixed;
559 var d = sym.definition();
560 var safe = safe_to_assign(tw, d, sym.scope, node.right);
561 d.assignments++;
562 var fixed = d.fixed;
563 if (!fixed && node.operator != "=") return;
564 var eq = node.operator == "=";
565 var value = eq ? node.right : node;
566 if (is_modified(compressor, tw, node, value, 0)) return;
567 if (!eq) d.chained = true;
568 sym.fixed = d.fixed = eq ? function() {
569 return node.right;
570 } : function() {
571 return make_node(AST_Binary, node, {
572 operator: node.operator.slice(0, -1),
573 left: fixed instanceof AST_Node ? fixed : fixed(),
574 right: node.right
575 });
576 };
577 if (!safe) return;
578 d.references.push(sym);
579 mark(tw, d, false);
580 node.right.walk(tw);
581 mark(tw, d, true);
582 if (eq) mark_escaped(tw, d, sym.scope, node, value, 0, 1);
583 return true;
584 });
585 def(AST_Binary, function(tw) {
586 if (!lazy_op[this.operator]) return;
587 this.left.walk(tw);
588 push(tw);
589 this.right.walk(tw);
590 pop(tw);
591 return true;
592 });
593 def(AST_Call, function(tw, descend) {
594 tw.find_parent(AST_Scope).may_call_this();
595 var exp = this.expression;
596 if (exp instanceof AST_SymbolRef) {
597 var def = exp.definition();
598 if (this.TYPE == "Call" && tw.in_boolean_context()) def.bool_fn++;
599 if (!(def.fixed instanceof AST_Defun)) return;
600 var defun = mark_defun(tw, def);
601 if (!defun) return;
602 descend();
603 defun.walk(tw);
604 return true;
605 } else if (this.TYPE == "Call"
606 && exp instanceof AST_Assign
607 && exp.operator == "="
608 && exp.left instanceof AST_SymbolRef
609 && tw.in_boolean_context()) {
610 exp.left.definition().bool_fn++;
611 }
612 });
613 def(AST_Case, function(tw) {
614 push(tw);
615 this.expression.walk(tw);
616 pop(tw);
617 push(tw);
618 walk_body(this, tw);
619 pop(tw);
620 return true;
621 });
622 def(AST_Conditional, function(tw) {
623 this.condition.walk(tw);
624 push(tw);
625 this.consequent.walk(tw);
626 pop(tw);
627 push(tw);
628 this.alternative.walk(tw);
629 pop(tw);
630 return true;
631 });
632 def(AST_Default, function(tw, descend) {
633 push(tw);
634 descend();
635 pop(tw);
636 return true;
637 });
638 def(AST_Defun, function(tw, descend, compressor) {
639 var id = this.name.definition().id;
640 if (tw.defun_visited[id]) return true;
641 if (tw.defun_ids[id] !== tw.safe_ids) return true;
642 tw.defun_visited[id] = true;
643 this.inlined = false;
644 push(tw);
645 reset_variables(tw, compressor, this);
646 descend();
647 pop(tw);
648 walk_defuns(tw, this);
649 return true;
650 });
651 def(AST_Do, function(tw) {
652 var saved_loop = tw.in_loop;
653 tw.in_loop = this;
654 push(tw);
655 this.body.walk(tw);
656 if (has_break_or_continue(this, tw.parent())) {
657 pop(tw);
658 push(tw);
659 }
660 this.condition.walk(tw);
661 pop(tw);
662 tw.in_loop = saved_loop;
663 return true;
664 });
665 def(AST_For, function(tw) {
666 if (this.init) this.init.walk(tw);
667 var saved_loop = tw.in_loop;
668 tw.in_loop = this;
669 push(tw);
670 if (this.condition) this.condition.walk(tw);
671 this.body.walk(tw);
672 if (this.step) {
673 if (has_break_or_continue(this, tw.parent())) {
674 pop(tw);
675 push(tw);
676 }
677 this.step.walk(tw);
678 }
679 pop(tw);
680 tw.in_loop = saved_loop;
681 return true;
682 });
683 def(AST_ForIn, function(tw) {
684 this.init.walk(suppressor);
685 this.object.walk(tw);
686 var saved_loop = tw.in_loop;
687 tw.in_loop = this;
688 push(tw);
689 this.body.walk(tw);
690 pop(tw);
691 tw.in_loop = saved_loop;
692 return true;
693 });
694 def(AST_Function, function(tw, descend, compressor) {
695 var node = this;
696 node.inlined = false;
697 push(tw);
698 reset_variables(tw, compressor, node);
699 var iife;
700 if (!node.name
701 && (iife = tw.parent()) instanceof AST_Call
702 && iife.expression === node) {
703 // Virtually turn IIFE parameters into variable definitions:
704 // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
705 // So existing transformation rules can work on them.
706 node.argnames.forEach(function(arg, i) {
707 var d = arg.definition();
708 if (d.fixed === undefined && (!node.uses_arguments || tw.has_directive("use strict"))) {
709 var value = iife.args[i];
710 d.fixed = function() {
711 var j = node.argnames.indexOf(arg);
712 if (j < 0) return value;
713 return iife.args[j] || make_node(AST_Undefined, iife);
714 };
715 tw.loop_ids[d.id] = tw.in_loop;
716 mark(tw, d, true);
717 } else {
718 d.fixed = false;
719 }
720 });
721 }
722 descend();
723 pop(tw);
724 walk_defuns(tw, node);
725 return true;
726 });
727 def(AST_If, function(tw) {
728 this.condition.walk(tw);
729 push(tw);
730 this.body.walk(tw);
731 pop(tw);
732 if (this.alternative) {
733 push(tw);
734 this.alternative.walk(tw);
735 pop(tw);
736 }
737 return true;
738 });
739 def(AST_LabeledStatement, function(tw) {
740 push(tw);
741 this.body.walk(tw);
742 pop(tw);
743 return true;
744 });
745 def(AST_SymbolCatch, function() {
746 this.definition().fixed = false;
747 });
748 def(AST_SymbolRef, function(tw, descend, compressor) {
749 if (this.fixed) delete this.fixed;
750 var d = this.definition();
751 d.references.push(this);
752 if (d.references.length == 1
753 && !d.fixed
754 && d.orig[0] instanceof AST_SymbolDefun) {
755 tw.loop_ids[d.id] = tw.in_loop;
756 }
757 var value;
758 if (d.fixed === undefined || !safe_to_read(tw, d)) {
759 d.fixed = false;
760 } else if (d.fixed) {
761 value = this.fixed_value();
762 var recursive = recursive_ref(tw, d);
763 if (recursive) {
764 d.recursive_refs++;
765 } else if (value && ref_once(tw, compressor, d)) {
766 d.single_use = value instanceof AST_Lambda && !value.pinned()
767 || d.scope === this.scope && value.is_constant_expression();
768 } else {
769 d.single_use = false;
770 }
771 if (is_modified(compressor, tw, this, value, 0, is_immutable(value), recursive)) {
772 if (d.single_use) {
773 d.single_use = "m";
774 } else {
775 d.fixed = false;
776 }
777 }
778 if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) {
779 d.cross_loop = true;
780 }
781 mark_escaped(tw, d, this.scope, this, value, 0, 1);
782 }
783 var parent;
784 if (d.fixed instanceof AST_Defun
785 && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
786 var defun = mark_defun(tw, d);
787 if (defun) defun.walk(tw);
788 }
789 });
790 def(AST_Toplevel, function(tw, descend, compressor) {
791 this.globals.each(function(def) {
792 reset_def(tw, compressor, def);
793 });
794 push(tw);
795 reset_variables(tw, compressor, this);
796 descend();
797 pop(tw);
798 walk_defuns(tw, this);
799 return true;
800 });
801 def(AST_Try, function(tw) {
802 push(tw);
803 walk_body(this, tw);
804 pop(tw);
805 if (this.bcatch) {
806 push(tw);
807 this.bcatch.walk(tw);
808 pop(tw);
809 }
810 if (this.bfinally) this.bfinally.walk(tw);
811 return true;
812 });
813 def(AST_Unary, function(tw, descend) {
814 var node = this;
815 if (!unary_arithmetic[node.operator]) return;
816 var exp = node.expression;
817 if (!(exp instanceof AST_SymbolRef)) {
818 mark_assignment_to_arguments(exp);
819 return;
820 }
821 if (exp.fixed) delete exp.fixed;
822 var d = exp.definition();
823 var safe = safe_to_assign(tw, d, exp.scope, true);
824 d.assignments++;
825 var fixed = d.fixed;
826 if (!fixed) return;
827 d.chained = true;
828 exp.fixed = d.fixed = function() {
829 return make_node(AST_Binary, node, {
830 operator: node.operator.slice(0, -1),
831 left: make_node(AST_UnaryPrefix, node, {
832 operator: "+",
833 expression: fixed instanceof AST_Node ? fixed : fixed()
834 }),
835 right: make_node(AST_Number, node, {
836 value: 1
837 })
838 });
839 };
840 if (!safe) return;
841 d.references.push(exp);
842 mark(tw, d, true);
843 return true;
844 });
845 def(AST_VarDef, function(tw, descend) {
846 var node = this;
847 var d = node.name.definition();
848 if (node.value) {
849 if (safe_to_assign(tw, d, node.name.scope, node.value)) {
850 d.fixed = function() {
851 return node.value;
852 };
853 tw.loop_ids[d.id] = tw.in_loop;
854 mark(tw, d, false);
855 descend();
856 mark(tw, d, true);
857 return true;
858 } else {
859 d.fixed = false;
860 }
861 }
862 });
863 def(AST_While, function(tw, descend) {
864 var saved_loop = tw.in_loop;
865 tw.in_loop = this;
866 push(tw);
867 descend();
868 pop(tw);
869 tw.in_loop = saved_loop;
870 return true;
871 });
872 })(function(node, func) {
873 node.DEFMETHOD("reduce_vars", func);
874 });
875
876 AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
877 var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
878 reset_flags(node);
879 return node.reduce_vars(tw, descend, compressor);
880 } : reset_flags);
881 // Flow control for visiting `AST_Defun`s
882 tw.defun_ids = Object.create(null);
883 tw.defun_visited = Object.create(null);
884 // Record the loop body in which `AST_SymbolDeclaration` is first encountered
885 tw.in_loop = null;
886 tw.loop_ids = Object.create(null);
887 // Stack of look-up tables to keep track of whether a `SymbolDef` has been
888 // properly assigned before use:
889 // - `push()` & `pop()` when visiting conditional branches
890 // - backup & restore via `save_ids` when visiting out-of-order sections
891 tw.safe_ids = Object.create(null);
892 this.walk(tw);
893
894 function reset_flags(node) {
895 node._squeezed = false;
896 node._optimized = false;
897 if (node instanceof AST_Scope) delete node._var_names;
898 }
899 });
900
901 AST_Symbol.DEFMETHOD("fixed_value", function(final) {
902 var fixed = this.definition().fixed;
903 if (!fixed) return fixed;
904 if (!final && this.fixed) fixed = this.fixed;
905 return fixed instanceof AST_Node ? fixed : fixed();
906 });
907
908 AST_SymbolRef.DEFMETHOD("is_immutable", function() {
909 var def = this.definition();
910 if (def.orig.length != 1) return false;
911 var sym = def.orig[0];
912 return sym instanceof AST_SymbolLambda && def.scope.name === sym;
913 });
914
915 function is_lhs_read_only(lhs, compressor) {
916 if (lhs instanceof AST_This) return true;
917 if (lhs instanceof AST_SymbolRef) {
918 var def = lhs.definition();
919 return def.lambda || compressor.exposed(def) && identifier_atom[def.name];
920 }
921 if (lhs instanceof AST_PropAccess) {
922 lhs = lhs.expression;
923 if (lhs instanceof AST_SymbolRef) {
924 if (lhs.is_immutable()) return false;
925 lhs = lhs.fixed_value();
926 }
927 if (!lhs) return true;
928 if (lhs.is_constant()) return true;
929 return is_lhs_read_only(lhs, compressor);
930 }
931 return false;
932 }
933
934 function find_variable(compressor, name) {
935 var scope, i = 0;
936 while (scope = compressor.parent(i++)) {
937 if (scope instanceof AST_Scope) break;
938 if (scope instanceof AST_Catch) {
939 scope = scope.argname.definition().scope;
940 break;
941 }
942 }
943 return scope.find_variable(name);
944 }
945
946 function make_node(ctor, orig, props) {
947 if (!props) props = {};
948 if (orig) {
949 if (!props.start) props.start = orig.start;
950 if (!props.end) props.end = orig.end;
951 }
952 return new ctor(props);
953 }
954
955 function make_sequence(orig, expressions) {
956 if (expressions.length == 1) return expressions[0];
957 return make_node(AST_Sequence, orig, {
958 expressions: expressions.reduce(merge_sequence, [])
959 });
960 }
961
962 function make_node_from_constant(val, orig) {
963 switch (typeof val) {
964 case "string":
965 return make_node(AST_String, orig, {
966 value: val
967 });
968 case "number":
969 if (isNaN(val)) return make_node(AST_NaN, orig);
970 if (isFinite(val)) {
971 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
972 operator: "-",
973 expression: make_node(AST_Number, orig, { value: -val })
974 }) : make_node(AST_Number, orig, { value: val });
975 }
976 return val < 0 ? make_node(AST_UnaryPrefix, orig, {
977 operator: "-",
978 expression: make_node(AST_Infinity, orig)
979 }) : make_node(AST_Infinity, orig);
980 case "boolean":
981 return make_node(val ? AST_True : AST_False, orig);
982 case "undefined":
983 return make_node(AST_Undefined, orig);
984 default:
985 if (val === null) {
986 return make_node(AST_Null, orig, { value: null });
987 }
988 if (val instanceof RegExp) {
989 return make_node(AST_RegExp, orig, { value: val });
990 }
991 throw new Error(string_template("Can't handle constant of type: {type}", {
992 type: typeof val
993 }));
994 }
995 }
996
997 function needs_unbinding(compressor, val) {
998 return val instanceof AST_PropAccess
999 || is_undeclared_ref(val) && val.name == "eval";
1000 }
1001
1002 // we shouldn't compress (1,func)(something) to
1003 // func(something) because that changes the meaning of
1004 // the func (becomes lexical instead of global).
1005 function maintain_this_binding(compressor, parent, orig, val) {
1006 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
1007 || parent.TYPE == "Call" && parent.expression === orig && needs_unbinding(compressor, val)) {
1008 return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
1009 }
1010 return val;
1011 }
1012
1013 function merge_sequence(array, node) {
1014 if (node instanceof AST_Sequence) {
1015 array.push.apply(array, node.expressions);
1016 } else {
1017 array.push(node);
1018 }
1019 return array;
1020 }
1021
1022 function as_statement_array(thing) {
1023 if (thing === null) return [];
1024 if (thing instanceof AST_BlockStatement) return thing.body;
1025 if (thing instanceof AST_EmptyStatement) return [];
1026 if (thing instanceof AST_Statement) return [ thing ];
1027 throw new Error("Can't convert thing to statement array");
1028 }
1029
1030 function is_empty(thing) {
1031 if (thing === null) return true;
1032 if (thing instanceof AST_EmptyStatement) return true;
1033 if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
1034 return false;
1035 }
1036
1037 function loop_body(x) {
1038 if (x instanceof AST_IterationStatement) {
1039 return x.body instanceof AST_BlockStatement ? x.body : x;
1040 }
1041 return x;
1042 }
1043
1044 function root_expr(prop) {
1045 while (prop instanceof AST_PropAccess) prop = prop.expression;
1046 return prop;
1047 }
1048
1049 function is_iife_call(node) {
1050 if (node.TYPE != "Call") return false;
1051 return node.expression instanceof AST_Function || is_iife_call(node.expression);
1052 }
1053
1054 function is_undeclared_ref(node) {
1055 return node instanceof AST_SymbolRef && node.definition().undeclared;
1056 }
1057
1058 var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Math Number parseFloat parseInt RangeError ReferenceError RegExp Object setInterval setTimeout String SyntaxError TypeError unescape URIError");
1059 AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
1060 return this.defined
1061 || !this.definition().undeclared
1062 || compressor.option("unsafe") && global_names[this.name];
1063 });
1064
1065 var identifier_atom = makePredicate("Infinity NaN undefined");
1066 function is_identifier_atom(node) {
1067 return node instanceof AST_Infinity
1068 || node instanceof AST_NaN
1069 || node instanceof AST_Undefined;
1070 }
1071
1072 function tighten_body(statements, compressor) {
1073 var in_loop, in_try, scope;
1074 find_loop_scope_try();
1075 var CHANGED, max_iter = 10;
1076 do {
1077 CHANGED = false;
1078 eliminate_spurious_blocks(statements);
1079 if (compressor.option("dead_code")) {
1080 eliminate_dead_code(statements, compressor);
1081 }
1082 if (compressor.option("if_return")) {
1083 handle_if_return(statements, compressor);
1084 }
1085 if (compressor.sequences_limit > 0) {
1086 sequencesize(statements, compressor);
1087 sequencesize_2(statements, compressor);
1088 }
1089 if (compressor.option("join_vars")) {
1090 join_consecutive_vars(statements);
1091 }
1092 if (compressor.option("collapse_vars")) {
1093 collapse(statements, compressor);
1094 }
1095 } while (CHANGED && max_iter-- > 0);
1096 return statements;
1097
1098 function find_loop_scope_try() {
1099 var node = compressor.self(), level = 0;
1100 do {
1101 if (node instanceof AST_Catch || node instanceof AST_Finally) {
1102 level++;
1103 } else if (node instanceof AST_IterationStatement) {
1104 in_loop = true;
1105 } else if (node instanceof AST_Scope) {
1106 scope = node;
1107 break;
1108 } else if (node instanceof AST_Try) {
1109 in_try = node;
1110 }
1111 } while (node = compressor.parent(level++));
1112 }
1113
1114 // Search from right to left for assignment-like expressions:
1115 // - `var a = x;`
1116 // - `a = x;`
1117 // - `++a`
1118 // For each candidate, scan from left to right for first usage, then try
1119 // to fold assignment into the site for compression.
1120 // Will not attempt to collapse assignments into or past code blocks
1121 // which are not sequentially executed, e.g. loops and conditionals.
1122 function collapse(statements, compressor) {
1123 if (scope.pinned()) return statements;
1124 var args;
1125 var candidates = [];
1126 var stat_index = statements.length;
1127 var scanner = new TreeTransformer(function(node, descend) {
1128 if (abort) return node;
1129 // Skip nodes before `candidate` as quickly as possible
1130 if (!hit) {
1131 if (node !== hit_stack[hit_index]) return node;
1132 hit_index++;
1133 if (hit_index < hit_stack.length) return handle_custom_scan_order(node);
1134 hit = true;
1135 stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
1136 if (stop_after === node) abort = true;
1137 return node;
1138 }
1139 // Stop immediately if these node types are encountered
1140 var parent = scanner.parent();
1141 if (should_stop(node, parent)) {
1142 abort = true;
1143 return node;
1144 }
1145 // Stop only if candidate is found within conditional branches
1146 if (!stop_if_hit && in_conditional(node, parent)) {
1147 stop_if_hit = parent;
1148 }
1149 // Skip transient nodes caused by single-use variable replacement
1150 if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
1151 // Replace variable with assignment when found
1152 var hit_rhs;
1153 if (!(node instanceof AST_SymbolDeclaration)
1154 && (scan_lhs && lhs.equivalent_to(node)
1155 || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
1156 if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
1157 if (!hit_rhs || !value_def) abort = true;
1158 return node;
1159 }
1160 if (is_lhs(node, parent)) {
1161 if (value_def && !hit_rhs) {
1162 assign_used = true;
1163 replaced++;
1164 }
1165 return node;
1166 } else if (value_def) {
1167 if (!hit_rhs) replaced++;
1168 return node;
1169 } else {
1170 replaced++;
1171 }
1172 CHANGED = abort = true;
1173 AST_Node.info("Collapsing {name} [{file}:{line},{col}]", {
1174 name: node.print_to_string(),
1175 file: node.start.file,
1176 line: node.start.line,
1177 col: node.start.col
1178 });
1179 if (candidate instanceof AST_UnaryPostfix) {
1180 return make_node(AST_UnaryPrefix, candidate, candidate);
1181 }
1182 if (candidate instanceof AST_VarDef) {
1183 var def = candidate.name.definition();
1184 if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
1185 def.replaced++;
1186 return maintain_this_binding(compressor, parent, node, candidate.value);
1187 }
1188 return make_node(AST_Assign, candidate, {
1189 operator: "=",
1190 left: make_node(AST_SymbolRef, candidate.name, candidate.name),
1191 right: candidate.value
1192 });
1193 }
1194 candidate.write_only = false;
1195 return candidate;
1196 }
1197 // These node types have child nodes that execute sequentially,
1198 // but are otherwise not safe to scan into or beyond them.
1199 if (is_last_node(node, parent) || may_throw(node)) {
1200 stop_after = node;
1201 if (node instanceof AST_Scope) abort = true;
1202 }
1203 // Scan but don't replace inside getter/setter
1204 if (node instanceof AST_Accessor) {
1205 var replace = can_replace;
1206 can_replace = false;
1207 descend(node, scanner);
1208 can_replace = replace;
1209 return node;
1210 }
1211 return handle_custom_scan_order(node);
1212 }, function(node) {
1213 if (abort) return;
1214 if (stop_after === node) abort = true;
1215 if (stop_if_hit === node) stop_if_hit = null;
1216 });
1217 var multi_replacer = new TreeTransformer(function(node) {
1218 if (abort) return node;
1219 // Skip nodes before `candidate` as quickly as possible
1220 if (!hit) {
1221 if (node !== hit_stack[hit_index]) return node;
1222 hit_index++;
1223 switch (hit_stack.length - hit_index) {
1224 case 0:
1225 hit = true;
1226 if (assign_used) return node;
1227 if (node instanceof AST_VarDef) return node;
1228 def.replaced++;
1229 var parent = multi_replacer.parent();
1230 if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
1231 value_def.replaced++;
1232 return List.skip;
1233 }
1234 return get_rvalue(candidate);
1235 case 1:
1236 if (!assign_used && node.body === candidate) {
1237 hit = true;
1238 def.replaced++;
1239 value_def.replaced++;
1240 return null;
1241 }
1242 default:
1243 return;
1244 }
1245 }
1246 // Replace variable when found
1247 if (node instanceof AST_SymbolRef
1248 && node.name == def.name) {
1249 if (!--replaced) abort = true;
1250 if (is_lhs(node, multi_replacer.parent())) return node;
1251 def.replaced++;
1252 value_def.replaced--;
1253 return get_rvalue(candidate).clone();
1254 }
1255 // Skip (non-executed) functions and (leading) default case in switch statements
1256 if (node instanceof AST_Default || node instanceof AST_Scope) return node;
1257 }, patch_sequence);
1258 var force_single;
1259 while (--stat_index >= 0) {
1260 // Treat parameters as collapsible in IIFE, i.e.
1261 // function(a, b){ ... }(x());
1262 // would be translated into equivalent assignments:
1263 // var a = x(), b = undefined;
1264 if (stat_index == 0 && compressor.option("unused")) extract_args();
1265 // Find collapsible assignments
1266 var hit_stack = [];
1267 extract_candidates(statements[stat_index]);
1268 while (candidates.length > 0) {
1269 hit_stack = candidates.pop();
1270 var hit_index = 0;
1271 var candidate = hit_stack[hit_stack.length - 1];
1272 var value_def = null;
1273 var stop_after = null;
1274 var stop_if_hit = null;
1275 var lhs = get_lhs(candidate);
1276 var side_effects = lhs && lhs.has_side_effects(compressor);
1277 var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
1278 var scan_rhs = foldable(get_rhs(candidate));
1279 if (!scan_lhs && !scan_rhs) continue;
1280 var modify_toplevel = false;
1281 // Locate symbols which may execute code outside of scanning range
1282 var lvalues = get_lvalues(candidate);
1283 var lhs_local = is_lhs_local(lhs);
1284 if (!side_effects) side_effects = value_has_side_effects(candidate);
1285 var replace_all = replace_all_symbols(candidate);
1286 var may_throw = candidate.may_throw(compressor) ? in_try ? function(node) {
1287 return node.has_side_effects(compressor);
1288 } : side_effects_external : return_false;
1289 var funarg = candidate.name instanceof AST_SymbolFunarg;
1290 var hit = funarg;
1291 var abort = false;
1292 var replaced = 0;
1293 var assign_used = false;
1294 var can_replace = !args || !hit;
1295 if (!can_replace) {
1296 for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
1297 args[j].transform(scanner);
1298 }
1299 can_replace = true;
1300 }
1301 for (var i = stat_index; !abort && i < statements.length; i++) {
1302 statements[i].transform(scanner);
1303 }
1304 if (value_def) {
1305 var def = lhs.definition();
1306 var referenced = def.references.length - def.replaced;
1307 if (candidate instanceof AST_Assign) referenced--;
1308 if (replaced && referenced == replaced) {
1309 abort = false;
1310 } else if (candidate instanceof AST_Assign) {
1311 candidates.push(hit_stack);
1312 force_single = true;
1313 continue;
1314 } else {
1315 replaced = false;
1316 }
1317 if (replaced) {
1318 hit_index = 0;
1319 hit = funarg;
1320 for (var i = stat_index; !abort && i < statements.length; i++) {
1321 if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
1322 }
1323 value_def.single_use = false;
1324 }
1325 }
1326 if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
1327 }
1328 }
1329
1330 function handle_custom_scan_order(node) {
1331 // Skip (non-executed) functions
1332 if (node instanceof AST_Scope) return node;
1333 // Scan case expressions first in a switch statement
1334 if (node instanceof AST_Switch) {
1335 node.expression = node.expression.transform(scanner);
1336 for (var i = 0; !abort && i < node.body.length; i++) {
1337 var branch = node.body[i];
1338 if (branch instanceof AST_Case) {
1339 if (!hit) {
1340 if (branch !== hit_stack[hit_index]) continue;
1341 hit_index++;
1342 }
1343 branch.expression = branch.expression.transform(scanner);
1344 if (!replace_all) break;
1345 scan_rhs = false;
1346 }
1347 }
1348 abort = true;
1349 return node;
1350 }
1351 }
1352
1353 function should_stop(node, parent) {
1354 if (parent instanceof AST_For) return node !== parent.init;
1355 if (node instanceof AST_Assign) {
1356 return node.operator != "=" && lhs.equivalent_to(node.left);
1357 }
1358 if (node instanceof AST_Call) {
1359 if (!(lhs instanceof AST_PropAccess)) return false;
1360 if (!lhs.equivalent_to(node.expression)) return false;
1361 var rhs = get_rvalue(candidate);
1362 return !(rhs instanceof AST_Function && !rhs.contains_this());
1363 }
1364 if (node instanceof AST_Debugger) return true;
1365 if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
1366 if (node instanceof AST_IterationStatement) return !(node instanceof AST_For);
1367 if (node instanceof AST_LoopControl) return true;
1368 if (node instanceof AST_Try) return true;
1369 if (node instanceof AST_With) return true;
1370 if (replace_all) return false;
1371 return node instanceof AST_SymbolRef
1372 && !node.is_declared(compressor)
1373 && !(parent instanceof AST_Assign && parent.operator == "=" && parent.left === node);
1374 }
1375
1376 function in_conditional(node, parent) {
1377 if (parent instanceof AST_Binary) return lazy_op[parent.operator] && parent.left !== node;
1378 if (parent instanceof AST_Case) return parent.expression !== node;
1379 if (parent instanceof AST_Conditional) return parent.condition !== node;
1380 return parent instanceof AST_If && parent.condition !== node;
1381 }
1382
1383 function is_last_node(node, parent) {
1384 if (node instanceof AST_Call) {
1385 var fn = node.expression;
1386 if (fn instanceof AST_SymbolRef) {
1387 if (fn.definition().recursive_refs > 0) return true;
1388 fn = fn.fixed_value();
1389 }
1390 if (!(fn instanceof AST_Lambda)) return true;
1391 if (fn.collapse_scanning) return false;
1392 fn.collapse_scanning = true;
1393 var replace = can_replace;
1394 can_replace = false;
1395 var after = stop_after;
1396 var if_hit = stop_if_hit;
1397 var rhs_fn = scan_rhs;
1398 for (var i = 0; !abort && i < fn.body.length; i++) {
1399 var stat = fn.body[i];
1400 if (stat instanceof AST_Return) {
1401 if (stat.value) stat.value.transform(scanner);
1402 break;
1403 }
1404 stat.transform(scanner);
1405 }
1406 scan_rhs = rhs_fn;
1407 stop_if_hit = if_hit;
1408 stop_after = after;
1409 can_replace = replace;
1410 delete fn.collapse_scanning;
1411 if (!abort) return false;
1412 abort = false;
1413 return true;
1414 }
1415 if (node instanceof AST_Exit) {
1416 if (in_try) {
1417 if (in_try.bfinally) return true;
1418 if (in_try.bcatch && node instanceof AST_Throw) return true;
1419 }
1420 return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
1421 }
1422 if (node instanceof AST_Function) {
1423 return compressor.option("ie8") && node.name && lvalues.has(node.name.name);
1424 }
1425 if (node instanceof AST_PropAccess) {
1426 return side_effects || !value_def && node.expression.may_throw_on_access(compressor);
1427 }
1428 if (node instanceof AST_SymbolRef) {
1429 if (symbol_in_lvalues(node, parent)) {
1430 return !parent || parent.operator != "=" || parent.left !== node;
1431 }
1432 return side_effects && may_modify(node);
1433 }
1434 if (node instanceof AST_This) return symbol_in_lvalues(node, parent);
1435 if (node instanceof AST_VarDef) {
1436 if (!node.value) return false;
1437 return lvalues.has(node.name.name) || side_effects && may_modify(node.name);
1438 }
1439 var sym = is_lhs(node.left, node);
1440 if (sym && lvalues.has(sym.name)) return true;
1441 if (sym instanceof AST_PropAccess) return true;
1442 }
1443
1444 function extract_args() {
1445 var iife, fn = compressor.self();
1446 if (fn instanceof AST_Function
1447 && !fn.name
1448 && !fn.uses_arguments
1449 && !fn.pinned()
1450 && (iife = compressor.parent()) instanceof AST_Call
1451 && iife.expression === fn) {
1452 var fn_strict = compressor.has_directive("use strict");
1453 if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
1454 var len = fn.argnames.length;
1455 args = iife.args.slice(len);
1456 var names = Object.create(null);
1457 for (var i = len; --i >= 0;) {
1458 var sym = fn.argnames[i];
1459 var arg = iife.args[i];
1460 args.unshift(make_node(AST_VarDef, sym, {
1461 name: sym,
1462 value: arg
1463 }));
1464 if (sym.name in names) continue;
1465 names[sym.name] = true;
1466 if (!arg) {
1467 arg = make_node(AST_Undefined, sym).transform(compressor);
1468 } else if (arg instanceof AST_Lambda && arg.pinned()) {
1469 arg = null;
1470 } else {
1471 arg.walk(new TreeWalker(function(node) {
1472 if (!arg) return true;
1473 if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
1474 var s = node.definition().scope;
1475 if (s !== scope) while (s = s.parent_scope) {
1476 if (s === scope) return true;
1477 }
1478 arg = null;
1479 }
1480 if (node instanceof AST_This && (fn_strict || !this.find_parent(AST_Scope))) {
1481 arg = null;
1482 return true;
1483 }
1484 }));
1485 }
1486 if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
1487 name: sym,
1488 value: arg
1489 }) ]);
1490 }
1491 }
1492 }
1493
1494 function extract_candidates(expr) {
1495 hit_stack.push(expr);
1496 if (expr instanceof AST_Array) {
1497 expr.elements.forEach(extract_candidates);
1498 } else if (expr instanceof AST_Assign) {
1499 candidates.push(hit_stack.slice());
1500 extract_candidates(expr.left);
1501 extract_candidates(expr.right);
1502 } else if (expr instanceof AST_Binary) {
1503 extract_candidates(expr.left);
1504 extract_candidates(expr.right);
1505 } else if (expr instanceof AST_Call) {
1506 extract_candidates(expr.expression);
1507 expr.args.forEach(extract_candidates);
1508 } else if (expr instanceof AST_Case) {
1509 extract_candidates(expr.expression);
1510 } else if (expr instanceof AST_Conditional) {
1511 extract_candidates(expr.condition);
1512 extract_candidates(expr.consequent);
1513 extract_candidates(expr.alternative);
1514 } else if (expr instanceof AST_Definitions) {
1515 expr.definitions.forEach(extract_candidates);
1516 } else if (expr instanceof AST_Dot) {
1517 extract_candidates(expr.expression);
1518 } else if (expr instanceof AST_DWLoop) {
1519 extract_candidates(expr.condition);
1520 if (!(expr.body instanceof AST_Block)) {
1521 extract_candidates(expr.body);
1522 }
1523 } else if (expr instanceof AST_Exit) {
1524 if (expr.value) extract_candidates(expr.value);
1525 } else if (expr instanceof AST_For) {
1526 if (expr.init) extract_candidates(expr.init);
1527 if (expr.condition) extract_candidates(expr.condition);
1528 if (expr.step) extract_candidates(expr.step);
1529 if (!(expr.body instanceof AST_Block)) {
1530 extract_candidates(expr.body);
1531 }
1532 } else if (expr instanceof AST_ForIn) {
1533 extract_candidates(expr.object);
1534 if (!(expr.body instanceof AST_Block)) {
1535 extract_candidates(expr.body);
1536 }
1537 } else if (expr instanceof AST_If) {
1538 extract_candidates(expr.condition);
1539 if (!(expr.body instanceof AST_Block)) {
1540 extract_candidates(expr.body);
1541 }
1542 if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
1543 extract_candidates(expr.alternative);
1544 }
1545 } else if (expr instanceof AST_Object) {
1546 expr.properties.forEach(function(prop) {
1547 if (prop instanceof AST_ObjectKeyVal) {
1548 hit_stack.push(prop);
1549 extract_candidates(prop.value);
1550 hit_stack.pop();
1551 }
1552 });
1553 } else if (expr instanceof AST_Sequence) {
1554 expr.expressions.forEach(extract_candidates);
1555 } else if (expr instanceof AST_SimpleStatement) {
1556 extract_candidates(expr.body);
1557 } else if (expr instanceof AST_Sub) {
1558 extract_candidates(expr.expression);
1559 extract_candidates(expr.property);
1560 } else if (expr instanceof AST_Switch) {
1561 extract_candidates(expr.expression);
1562 expr.body.forEach(extract_candidates);
1563 } else if (expr instanceof AST_Unary) {
1564 if (unary_arithmetic[expr.operator]) {
1565 candidates.push(hit_stack.slice());
1566 } else {
1567 extract_candidates(expr.expression);
1568 }
1569 } else if (expr instanceof AST_VarDef) {
1570 if (expr.value) {
1571 var def = expr.name.definition();
1572 if (def.references.length > def.replaced) {
1573 candidates.push(hit_stack.slice());
1574 }
1575 extract_candidates(expr.value);
1576 }
1577 }
1578 hit_stack.pop();
1579 }
1580
1581 function find_stop(node, level) {
1582 var parent = scanner.parent(level);
1583 if (parent instanceof AST_Array) return node;
1584 if (parent instanceof AST_Assign) return node;
1585 if (parent instanceof AST_Binary) return node;
1586 if (parent instanceof AST_Call) return node;
1587 if (parent instanceof AST_Case) return node;
1588 if (parent instanceof AST_Conditional) return node;
1589 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
1590 if (parent instanceof AST_Exit) return node;
1591 if (parent instanceof AST_If) return node;
1592 if (parent instanceof AST_IterationStatement) return node;
1593 if (parent instanceof AST_ObjectKeyVal) return node;
1594 if (parent instanceof AST_PropAccess) return node;
1595 if (parent instanceof AST_Sequence) {
1596 return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
1597 }
1598 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
1599 if (parent instanceof AST_Switch) return node;
1600 if (parent instanceof AST_Unary) return node;
1601 if (parent instanceof AST_VarDef) return node;
1602 return null;
1603 }
1604
1605 function find_stop_value(node, level) {
1606 var parent = scanner.parent(level);
1607 if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
1608 if (parent instanceof AST_Assign) {
1609 if (may_throw(parent)) return node;
1610 if (parent.left instanceof AST_SymbolRef) {
1611 var name = parent.left.name;
1612 if (lhs.name == name) return node;
1613 if (value_def.name == name) return node;
1614 }
1615 return find_stop_value(parent, level + 1);
1616 }
1617 if (parent instanceof AST_Binary) {
1618 if (lazy_op[parent.operator] && parent.left !== node) {
1619 do {
1620 node = parent;
1621 parent = scanner.parent(++level);
1622 } while (parent instanceof AST_Binary && parent.operator == node.operator);
1623 return node;
1624 }
1625 return find_stop_value(parent, level + 1);
1626 }
1627 if (parent instanceof AST_Call) return parent;
1628 if (parent instanceof AST_Case) {
1629 if (parent.expression !== node) return node;
1630 return find_stop_value(parent, level + 1);
1631 }
1632 if (parent instanceof AST_Conditional) {
1633 if (parent.condition !== node) return node;
1634 return find_stop_value(parent, level + 1);
1635 }
1636 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
1637 if (parent instanceof AST_Do) return node;
1638 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
1639 if (parent instanceof AST_For) {
1640 if (parent.init !== node && parent.condition !== node) return node;
1641 return find_stop_value(parent, level + 1);
1642 }
1643 if (parent instanceof AST_ForIn) {
1644 if (parent.init !== node) return node;
1645 return find_stop_value(parent, level + 1);
1646 }
1647 if (parent instanceof AST_If) {
1648 if (parent.condition !== node) return node;
1649 return find_stop_value(parent, level + 1);
1650 }
1651 if (parent instanceof AST_ObjectKeyVal) {
1652 var obj = scanner.parent(level + 1);
1653 return all(obj.properties, function(prop) {
1654 return prop instanceof AST_ObjectKeyVal;
1655 }) ? find_stop_value(obj, level + 2) : obj;
1656 }
1657 if (parent instanceof AST_PropAccess) return find_stop_value(parent, level + 1);
1658 if (parent instanceof AST_Sequence) {
1659 return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
1660 }
1661 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
1662 if (parent instanceof AST_Switch) {
1663 if (parent.expression !== node) return node;
1664 return find_stop_value(parent, level + 1);
1665 }
1666 if (parent instanceof AST_Unary) {
1667 if (parent.operator == "delete") return node;
1668 return find_stop_value(parent, level + 1);
1669 }
1670 if (parent instanceof AST_VarDef) {
1671 var name = parent.name.name;
1672 if (lhs.name == name) return node;
1673 if (value_def.name == name) return node;
1674 return find_stop_value(parent, level + 1);
1675 }
1676 if (parent instanceof AST_While) {
1677 if (parent.condition !== node) return node;
1678 return find_stop_value(parent, level + 1);
1679 }
1680 return null;
1681 }
1682
1683 function find_stop_unused(node, level) {
1684 var parent = scanner.parent(level);
1685 if (is_last_node(node, parent)) return node;
1686 if (in_conditional(node, parent)) return node;
1687 if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
1688 if (parent instanceof AST_Assign) {
1689 return may_throw(parent) ? node : find_stop_unused(parent, level + 1);
1690 }
1691 if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
1692 if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
1693 if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
1694 if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
1695 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
1696 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
1697 if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
1698 if (parent instanceof AST_IterationStatement) return node;
1699 if (parent instanceof AST_ObjectKeyVal) {
1700 var obj = scanner.parent(level + 1);
1701 return all(obj.properties, function(prop) {
1702 return prop instanceof AST_ObjectKeyVal;
1703 }) ? find_stop_unused(obj, level + 2) : obj;
1704 }
1705 if (parent instanceof AST_PropAccess) {
1706 var exp = parent.expression;
1707 if (exp === node) return find_stop_unused(parent, level + 1);
1708 var sym = root_expr(exp);
1709 if (!(sym instanceof AST_SymbolRef)) return find_stop_unused(parent, level + 1);
1710 var lvalue = lvalues.get(sym.name);
1711 return !lvalue || all(lvalue, function(lhs) {
1712 return !(lhs instanceof AST_PropAccess);
1713 }) ? find_stop_unused(parent, level + 1) : node;
1714 }
1715 if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
1716 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
1717 if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
1718 if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
1719 if (parent instanceof AST_VarDef) return find_stop_unused(parent, level + 1);
1720 return null;
1721 }
1722
1723 function mangleable_var(value) {
1724 if (force_single) {
1725 force_single = false;
1726 return;
1727 }
1728 if (!(value instanceof AST_SymbolRef)) return;
1729 var def = value.definition();
1730 if (def.undeclared) return;
1731 if (is_arguments(def)) return;
1732 return value_def = def;
1733 }
1734
1735 function get_lhs(expr) {
1736 if (expr instanceof AST_VarDef) {
1737 var def = expr.name.definition();
1738 if (!member(expr.name, def.orig)) return;
1739 var referenced = def.references.length - def.replaced;
1740 var declared = def.orig.length - def.eliminated;
1741 if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
1742 || (referenced > 1 ? mangleable_var(expr.value) : !compressor.exposed(def))) {
1743 return make_node(AST_SymbolRef, expr.name, expr.name);
1744 }
1745 } else if (expr instanceof AST_Assign) {
1746 var lhs = expr.left;
1747 if (expr.operator == "=" && lhs instanceof AST_SymbolRef) {
1748 var def = lhs.definition();
1749 if (def.references[0] === lhs) {
1750 var referenced = def.references.length - def.replaced;
1751 if (referenced > 1) mangleable_var(expr.right);
1752 }
1753 }
1754 return lhs;
1755 } else {
1756 return expr.expression;
1757 }
1758 }
1759
1760 function get_rhs(expr) {
1761 return candidate instanceof AST_Assign && candidate.operator == "=" && candidate.right;
1762 }
1763
1764 function get_rvalue(expr) {
1765 return expr[expr instanceof AST_Assign ? "right" : "value"];
1766 }
1767
1768 function invariant(expr) {
1769 if (expr instanceof AST_Array) return false;
1770 if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
1771 return invariant(expr.left) && invariant(expr.right);
1772 }
1773 if (expr instanceof AST_Call) return false;
1774 if (expr instanceof AST_Conditional) {
1775 return invariant(expr.consequent) && invariant(expr.alternative);
1776 }
1777 if (expr instanceof AST_Object) return false;
1778 return !expr.has_side_effects(compressor);
1779 }
1780
1781 function foldable(expr) {
1782 if (!expr) return false;
1783 if (expr instanceof AST_SymbolRef) {
1784 var value = expr.evaluate(compressor);
1785 if (value === expr) return rhs_exact_match;
1786 return rhs_fuzzy_match(value, rhs_exact_match);
1787 }
1788 if (expr instanceof AST_This) return rhs_exact_match;
1789 if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
1790 if (expr.is_constant()) {
1791 return rhs_fuzzy_match(expr.evaluate(compressor), rhs_exact_match);
1792 }
1793 if (!(lhs instanceof AST_SymbolRef)) return false;
1794 if (!invariant(expr)) return false;
1795 var circular;
1796 var def = lhs.definition();
1797 expr.walk(new TreeWalker(function(node) {
1798 if (circular) return true;
1799 if (node instanceof AST_SymbolRef && node.definition() === def) {
1800 circular = true;
1801 }
1802 }));
1803 return !circular && rhs_exact_match;
1804
1805 function rhs_exact_match(node) {
1806 return expr.equivalent_to(node);
1807 }
1808 }
1809
1810 function rhs_fuzzy_match(value, fallback) {
1811 return function(node, tw) {
1812 if (tw.in_boolean_context()) {
1813 if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
1814 return true;
1815 }
1816 if (node.is_constant()) {
1817 return !node.evaluate(compressor) == !value;
1818 }
1819 }
1820 return fallback(node);
1821 };
1822 }
1823
1824 function get_lvalues(expr) {
1825 var lvalues = new Dictionary();
1826 if (candidate instanceof AST_VarDef) lvalues.add(candidate.name.name, lhs);
1827 var scan_iife = scope instanceof AST_Toplevel;
1828 var tw = new TreeWalker(function(node) {
1829 if (scan_iife && node.TYPE == "Call") {
1830 var exp = node.expression;
1831 if (exp instanceof AST_PropAccess) return;
1832 if (exp instanceof AST_Function && !exp.contains_this()) return;
1833 modify_toplevel = true;
1834 scan_iife = false;
1835 return;
1836 }
1837 var value;
1838 if (node instanceof AST_SymbolRef) {
1839 value = node.fixed_value() || node;
1840 } else if (node instanceof AST_This) {
1841 value = node;
1842 }
1843 if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
1844 });
1845 expr.walk(tw);
1846 return lvalues;
1847 }
1848
1849 function remove_candidate(expr) {
1850 if (expr.name instanceof AST_SymbolFunarg) {
1851 var index = compressor.self().argnames.indexOf(expr.name);
1852 var args = compressor.parent().args;
1853 if (args[index]) args[index] = make_node(AST_Number, args[index], {
1854 value: 0
1855 });
1856 return true;
1857 }
1858 var found = false;
1859 return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
1860 if (found) return node;
1861 if (node instanceof AST_Scope) return node;
1862 if (node !== expr && node.body !== expr) return;
1863 found = true;
1864 if (node instanceof AST_VarDef) {
1865 node.value = null;
1866 return node;
1867 }
1868 return in_list ? List.skip : null;
1869 }, patch_sequence));
1870 }
1871
1872 function patch_sequence(node) {
1873 if (node instanceof AST_Sequence) switch (node.expressions.length) {
1874 case 0: return null;
1875 case 1: return node.expressions[0];
1876 }
1877 }
1878
1879 function is_lhs_local(lhs) {
1880 var sym = root_expr(lhs);
1881 return sym instanceof AST_SymbolRef
1882 && sym.definition().scope === scope
1883 && !(in_loop
1884 && (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
1885 || candidate instanceof AST_Unary
1886 || candidate instanceof AST_Assign && candidate.operator != "="));
1887 }
1888
1889 function value_has_side_effects(expr) {
1890 if (expr instanceof AST_Unary) return false;
1891 return get_rvalue(expr).has_side_effects(compressor);
1892 }
1893
1894 function replace_all_symbols(expr) {
1895 if (expr instanceof AST_Unary) return false;
1896 if (side_effects) return false;
1897 if (value_def) return true;
1898 if (!(lhs instanceof AST_SymbolRef)) return false;
1899 var referenced;
1900 if (expr instanceof AST_VarDef) {
1901 referenced = 1;
1902 } else if (expr.operator == "=") {
1903 referenced = 2;
1904 } else {
1905 return false;
1906 }
1907 var def = lhs.definition();
1908 return def.references.length - def.replaced == referenced;
1909 }
1910
1911 function symbol_in_lvalues(sym, parent) {
1912 var lvalue = lvalues.get(sym.name);
1913 if (!lvalue || all(lvalue, function(lhs) {
1914 return !lhs;
1915 })) return;
1916 if (lvalue[0] !== lhs) return true;
1917 scan_rhs = false;
1918 }
1919
1920 function may_modify(sym) {
1921 var def = sym.definition();
1922 if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
1923 if (def.scope !== scope) return true;
1924 if (modify_toplevel && compressor.exposed(def)) return true;
1925 return !all(def.references, function(ref) {
1926 return ref.scope.resolve() === scope;
1927 });
1928 }
1929
1930 function side_effects_external(node, lhs) {
1931 if (node instanceof AST_Assign) return side_effects_external(node.left, true);
1932 if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
1933 if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
1934 if (lhs) {
1935 if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
1936 if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
1937 if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
1938 }
1939 return false;
1940 }
1941 }
1942
1943 function eliminate_spurious_blocks(statements) {
1944 var seen_dirs = [];
1945 for (var i = 0; i < statements.length;) {
1946 var stat = statements[i];
1947 if (stat instanceof AST_BlockStatement) {
1948 CHANGED = true;
1949 eliminate_spurious_blocks(stat.body);
1950 [].splice.apply(statements, [i, 1].concat(stat.body));
1951 i += stat.body.length;
1952 } else if (stat instanceof AST_EmptyStatement) {
1953 CHANGED = true;
1954 statements.splice(i, 1);
1955 } else if (stat instanceof AST_Directive) {
1956 if (!member(stat.value, seen_dirs)) {
1957 i++;
1958 seen_dirs.push(stat.value);
1959 } else {
1960 CHANGED = true;
1961 statements.splice(i, 1);
1962 }
1963 } else i++;
1964 }
1965 }
1966
1967 function handle_if_return(statements, compressor) {
1968 var self = compressor.self();
1969 var multiple_if_returns = has_multiple_if_returns(statements);
1970 var in_lambda = self instanceof AST_Lambda;
1971 for (var i = statements.length; --i >= 0;) {
1972 var stat = statements[i];
1973 var j = next_index(i);
1974 var next = statements[j];
1975
1976 if (in_lambda && !next && stat instanceof AST_Return) {
1977 if (!stat.value) {
1978 CHANGED = true;
1979 statements.splice(i, 1);
1980 continue;
1981 }
1982 if (stat.value instanceof AST_UnaryPrefix && stat.value.operator == "void") {
1983 CHANGED = true;
1984 statements[i] = make_node(AST_SimpleStatement, stat, {
1985 body: stat.value.expression
1986 });
1987 continue;
1988 }
1989 }
1990
1991 if (stat instanceof AST_If) {
1992 var ab = aborts(stat.body);
1993 if (can_merge_flow(ab)) {
1994 if (ab.label) remove(ab.label.thedef.references, ab);
1995 CHANGED = true;
1996 stat = stat.clone();
1997 stat.condition = stat.condition.negate(compressor);
1998 var body = as_statement_array_with_return(stat.body, ab);
1999 stat.body = make_node(AST_BlockStatement, stat, {
2000 body: as_statement_array(stat.alternative).concat(extract_functions())
2001 });
2002 stat.alternative = make_node(AST_BlockStatement, stat, {
2003 body: body
2004 });
2005 statements[i] = stat;
2006 statements[i] = stat.transform(compressor);
2007 continue;
2008 }
2009
2010 if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) {
2011 var negated = stat.condition.negate(compressor);
2012 if (negated.print_to_string().length <= stat.condition.print_to_string().length) {
2013 CHANGED = true;
2014 stat = stat.clone();
2015 stat.condition = negated;
2016 statements[j] = stat.body;
2017 stat.body = next;
2018 statements[i] = stat;
2019 statements[i] = stat.transform(compressor);
2020 continue;
2021 }
2022 }
2023
2024 var alt = aborts(stat.alternative);
2025 if (can_merge_flow(alt)) {
2026 if (alt.label) remove(alt.label.thedef.references, alt);
2027 CHANGED = true;
2028 stat = stat.clone();
2029 stat.body = make_node(AST_BlockStatement, stat.body, {
2030 body: as_statement_array(stat.body).concat(extract_functions())
2031 });
2032 var body = as_statement_array_with_return(stat.alternative, alt);
2033 stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
2034 body: body
2035 });
2036 statements[i] = stat;
2037 statements[i] = stat.transform(compressor);
2038 continue;
2039 }
2040
2041 if (compressor.option("typeofs")) {
2042 if (ab && !alt) {
2043 mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
2044 body: statements.slice(i + 1)
2045 }));
2046 }
2047 if (!ab && alt) {
2048 mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
2049 body: statements.slice(i + 1)
2050 }));
2051 }
2052 }
2053 }
2054
2055 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
2056 var value = stat.body.value;
2057 var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
2058 //---
2059 // pretty silly case, but:
2060 // if (foo()) return; return; => foo(); return;
2061 if (!value && !stat.alternative
2062 && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
2063 CHANGED = true;
2064 statements[i] = make_node(AST_SimpleStatement, stat.condition, {
2065 body: stat.condition
2066 });
2067 continue;
2068 }
2069 //---
2070 // if (foo()) return x; return y; => return foo() ? x : y;
2071 if ((in_bool || value) && !stat.alternative && next instanceof AST_Return) {
2072 CHANGED = true;
2073 stat = stat.clone();
2074 stat.alternative = next;
2075 statements.splice(i, 1, stat.transform(compressor));
2076 statements.splice(j, 1);
2077 continue;
2078 }
2079 //---
2080 // if (foo()) return x; [ return ; ] => return foo() ? x : undefined;
2081 if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
2082 CHANGED = true;
2083 stat = stat.clone();
2084 stat.alternative = make_node(AST_Return, stat, {
2085 value: null
2086 });
2087 statements.splice(i, 1, stat.transform(compressor));
2088 continue;
2089 }
2090 //---
2091 // if (a) return b; if (c) return d; e; => return a ? b : c ? d : void e;
2092 //
2093 // if sequences is not enabled, this can lead to an endless loop (issue #866).
2094 // however, with sequences on this helps producing slightly better output for
2095 // the example code.
2096 var prev = statements[prev_index(i)];
2097 if (compressor.option("sequences") && in_lambda && !stat.alternative
2098 && prev instanceof AST_If && prev.body instanceof AST_Return
2099 && next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
2100 CHANGED = true;
2101 stat = stat.clone();
2102 stat.alternative = make_node(AST_BlockStatement, next, {
2103 body: [
2104 next,
2105 make_node(AST_Return, next, {
2106 value: null
2107 })
2108 ]
2109 });
2110 statements.splice(i, 1, stat.transform(compressor));
2111 statements.splice(j, 1);
2112 continue;
2113 }
2114 }
2115 }
2116
2117 function has_multiple_if_returns(statements) {
2118 var n = 0;
2119 for (var i = statements.length; --i >= 0;) {
2120 var stat = statements[i];
2121 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
2122 if (++n > 1) return true;
2123 }
2124 }
2125 return false;
2126 }
2127
2128 function is_return_void(value) {
2129 return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
2130 }
2131
2132 function can_merge_flow(ab) {
2133 if (!ab) return false;
2134 var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
2135 return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
2136 || ab instanceof AST_Continue && self === loop_body(lct)
2137 || ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
2138 }
2139
2140 function extract_functions() {
2141 var tail = statements.slice(i + 1);
2142 statements.length = i + 1;
2143 return tail.filter(function(stat) {
2144 if (stat instanceof AST_Defun) {
2145 statements.push(stat);
2146 return false;
2147 }
2148 return true;
2149 });
2150 }
2151
2152 function as_statement_array_with_return(node, ab) {
2153 var body = as_statement_array(node).slice(0, -1);
2154 if (ab.value) {
2155 body.push(make_node(AST_SimpleStatement, ab.value, {
2156 body: ab.value.expression
2157 }));
2158 }
2159 return body;
2160 }
2161
2162 function next_index(i) {
2163 for (var j = i + 1; j < statements.length; j++) {
2164 var stat = statements[j];
2165 if (!(stat instanceof AST_Var && declarations_only(stat))) {
2166 break;
2167 }
2168 }
2169 return j;
2170 }
2171
2172 function prev_index(i) {
2173 for (var j = i; --j >= 0;) {
2174 var stat = statements[j];
2175 if (!(stat instanceof AST_Var && declarations_only(stat))) {
2176 break;
2177 }
2178 }
2179 return j;
2180 }
2181 }
2182
2183 function eliminate_dead_code(statements, compressor) {
2184 var has_quit;
2185 var self = compressor.self();
2186 for (var i = 0, n = 0, len = statements.length; i < len; i++) {
2187 var stat = statements[i];
2188 if (stat instanceof AST_LoopControl) {
2189 var lct = compressor.loopcontrol_target(stat);
2190 if (stat instanceof AST_Break
2191 && !(lct instanceof AST_IterationStatement)
2192 && loop_body(lct) === self
2193 || stat instanceof AST_Continue
2194 && loop_body(lct) === self) {
2195 if (stat.label) remove(stat.label.thedef.references, stat);
2196 } else {
2197 statements[n++] = stat;
2198 }
2199 } else {
2200 statements[n++] = stat;
2201 }
2202 if (aborts(stat)) {
2203 has_quit = statements.slice(i + 1);
2204 break;
2205 }
2206 }
2207 statements.length = n;
2208 CHANGED = n != len;
2209 if (has_quit) has_quit.forEach(function(stat) {
2210 extract_declarations_from_unreachable_code(stat, statements);
2211 });
2212 }
2213
2214 function declarations_only(node) {
2215 return all(node.definitions, function(var_def) {
2216 return !var_def.value;
2217 });
2218 }
2219
2220 function sequencesize(statements, compressor) {
2221 if (statements.length < 2) return;
2222 var seq = [], n = 0;
2223 function push_seq() {
2224 if (!seq.length) return;
2225 var body = make_sequence(seq[0], seq);
2226 statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
2227 seq = [];
2228 }
2229 for (var i = 0, len = statements.length; i < len; i++) {
2230 var stat = statements[i];
2231 if (stat instanceof AST_SimpleStatement) {
2232 if (seq.length >= compressor.sequences_limit) push_seq();
2233 var body = stat.body;
2234 if (seq.length > 0) body = body.drop_side_effect_free(compressor);
2235 if (body) merge_sequence(seq, body);
2236 } else if (stat instanceof AST_Definitions && declarations_only(stat)
2237 || stat instanceof AST_Defun) {
2238 statements[n++] = stat;
2239 } else {
2240 push_seq();
2241 statements[n++] = stat;
2242 }
2243 }
2244 push_seq();
2245 statements.length = n;
2246 if (n != len) CHANGED = true;
2247 }
2248
2249 function to_simple_statement(block, decls) {
2250 if (!(block instanceof AST_BlockStatement)) return block;
2251 var stat = null;
2252 for (var i = 0; i < block.body.length; i++) {
2253 var line = block.body[i];
2254 if (line instanceof AST_Var && declarations_only(line)) {
2255 decls.push(line);
2256 } else if (stat) {
2257 return false;
2258 } else {
2259 stat = line;
2260 }
2261 }
2262 return stat;
2263 }
2264
2265 function sequencesize_2(statements, compressor) {
2266 function cons_seq(right) {
2267 n--;
2268 CHANGED = true;
2269 var left = prev.body;
2270 return make_sequence(left, [ left, right ]);
2271 }
2272 var n = 0, prev;
2273 for (var i = 0; i < statements.length; i++) {
2274 var stat = statements[i];
2275 if (prev) {
2276 if (stat instanceof AST_Exit) {
2277 stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).transform(compressor);
2278 } else if (stat instanceof AST_For) {
2279 if (!(stat.init instanceof AST_Definitions)) {
2280 var abort = false;
2281 prev.body.walk(new TreeWalker(function(node) {
2282 if (abort || node instanceof AST_Scope) return true;
2283 if (node instanceof AST_Binary && node.operator == "in") {
2284 abort = true;
2285 return true;
2286 }
2287 }));
2288 if (!abort) {
2289 if (stat.init) stat.init = cons_seq(stat.init);
2290 else {
2291 stat.init = prev.body;
2292 n--;
2293 CHANGED = true;
2294 }
2295 }
2296 }
2297 } else if (stat instanceof AST_ForIn) {
2298 stat.object = cons_seq(stat.object);
2299 } else if (stat instanceof AST_If) {
2300 stat.condition = cons_seq(stat.condition);
2301 } else if (stat instanceof AST_Switch) {
2302 stat.expression = cons_seq(stat.expression);
2303 } else if (stat instanceof AST_With) {
2304 stat.expression = cons_seq(stat.expression);
2305 }
2306 }
2307 if (compressor.option("conditionals") && stat instanceof AST_If) {
2308 var decls = [];
2309 var body = to_simple_statement(stat.body, decls);
2310 var alt = to_simple_statement(stat.alternative, decls);
2311 if (body !== false && alt !== false && decls.length > 0) {
2312 var len = decls.length;
2313 decls.push(make_node(AST_If, stat, {
2314 condition: stat.condition,
2315 body: body || make_node(AST_EmptyStatement, stat.body),
2316 alternative: alt
2317 }));
2318 decls.unshift(n, 1);
2319 [].splice.apply(statements, decls);
2320 i += len;
2321 n += len + 1;
2322 prev = null;
2323 CHANGED = true;
2324 continue;
2325 }
2326 }
2327 statements[n++] = stat;
2328 prev = stat instanceof AST_SimpleStatement ? stat : null;
2329 }
2330 statements.length = n;
2331 }
2332
2333 function join_assigns(defn, body) {
2334 var exprs;
2335 if (body instanceof AST_Assign) {
2336 exprs = [ body ];
2337 } else if (body instanceof AST_Sequence) {
2338 exprs = body.expressions.slice();
2339 }
2340 if (!exprs) return;
2341 if (defn instanceof AST_Definitions) {
2342 var def = defn.definitions[defn.definitions.length - 1];
2343 if (trim_assigns(def.name, def.value, exprs)) return exprs;
2344 }
2345 for (var i = exprs.length - 1; --i >= 0;) {
2346 var expr = exprs[i];
2347 if (!(expr instanceof AST_Assign)) continue;
2348 if (expr.operator != "=") continue;
2349 if (!(expr.left instanceof AST_SymbolRef)) continue;
2350 var tail = exprs.slice(i + 1);
2351 if (!trim_assigns(expr.left, expr.right, tail)) continue;
2352 return exprs.slice(0, i + 1).concat(tail);
2353 }
2354 }
2355
2356 function trim_assigns(name, value, exprs) {
2357 if (!(value instanceof AST_Object)) return;
2358 var trimmed = false;
2359 do {
2360 var node = exprs[0];
2361 if (!(node instanceof AST_Assign)) break;
2362 if (node.operator != "=") break;
2363 if (!(node.left instanceof AST_PropAccess)) break;
2364 var sym = node.left.expression;
2365 if (!(sym instanceof AST_SymbolRef)) break;
2366 if (name.name != sym.name) break;
2367 if (!node.right.is_constant_expression(scope)) break;
2368 var prop = node.left.property;
2369 if (prop instanceof AST_Node) {
2370 prop = prop.evaluate(compressor);
2371 }
2372 if (prop instanceof AST_Node) break;
2373 prop = "" + prop;
2374 var diff = compressor.has_directive("use strict") ? function(node) {
2375 return node.key != prop && node.key.name != prop;
2376 } : function(node) {
2377 return node.key.name != prop;
2378 };
2379 if (!all(value.properties, diff)) break;
2380 value.properties.push(make_node(AST_ObjectKeyVal, node, {
2381 key: prop,
2382 value: node.right
2383 }));
2384 exprs.shift();
2385 trimmed = true;
2386 } while (exprs.length);
2387 return trimmed;
2388 }
2389
2390 function join_consecutive_vars(statements) {
2391 var defs;
2392 for (var i = 0, j = -1; i < statements.length; i++) {
2393 var stat = statements[i];
2394 var prev = statements[j];
2395 if (stat instanceof AST_Definitions) {
2396 if (prev && prev.TYPE == stat.TYPE) {
2397 prev.definitions = prev.definitions.concat(stat.definitions);
2398 CHANGED = true;
2399 } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
2400 defs.definitions = defs.definitions.concat(stat.definitions);
2401 CHANGED = true;
2402 } else {
2403 statements[++j] = stat;
2404 defs = stat;
2405 }
2406 } else if (stat instanceof AST_Exit) {
2407 stat.value = join_assigns_expr(stat.value);
2408 } else if (stat instanceof AST_For) {
2409 var exprs = join_assigns(prev, stat.init);
2410 if (exprs) {
2411 CHANGED = true;
2412 stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
2413 statements[++j] = stat;
2414 } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
2415 if (stat.init) {
2416 prev.definitions = prev.definitions.concat(stat.init.definitions);
2417 }
2418 stat.init = prev;
2419 statements[j] = stat;
2420 CHANGED = true;
2421 } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
2422 defs.definitions = defs.definitions.concat(stat.init.definitions);
2423 stat.init = null;
2424 statements[++j] = stat;
2425 CHANGED = true;
2426 } else {
2427 statements[++j] = stat;
2428 }
2429 } else if (stat instanceof AST_ForIn) {
2430 stat.object = join_assigns_expr(stat.object);
2431 } else if (stat instanceof AST_If) {
2432 stat.condition = join_assigns_expr(stat.condition);
2433 } else if (stat instanceof AST_SimpleStatement) {
2434 var exprs = join_assigns(prev, stat.body);
2435 if (exprs) {
2436 CHANGED = true;
2437 if (!exprs.length) continue;
2438 stat.body = make_sequence(stat.body, exprs);
2439 }
2440 statements[++j] = stat;
2441 } else if (stat instanceof AST_Switch) {
2442 stat.expression = join_assigns_expr(stat.expression);
2443 } else if (stat instanceof AST_With) {
2444 stat.expression = join_assigns_expr(stat.expression);
2445 } else {
2446 statements[++j] = stat;
2447 }
2448 }
2449 statements.length = j + 1;
2450
2451 function join_assigns_expr(value) {
2452 statements[++j] = stat;
2453 var exprs = join_assigns(prev, value);
2454 if (!exprs) return value;
2455 CHANGED = true;
2456 var tail = value.tail_node();
2457 if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left);
2458 return make_sequence(value, exprs);
2459 }
2460 }
2461 }
2462
2463 function extract_declarations_from_unreachable_code(stat, target) {
2464 if (!(stat instanceof AST_Defun)) {
2465 AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
2466 }
2467 stat.walk(new TreeWalker(function(node) {
2468 if (node instanceof AST_Definitions) {
2469 AST_Node.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
2470 node.remove_initializers();
2471 target.push(node);
2472 return true;
2473 }
2474 if (node instanceof AST_Defun) {
2475 target.push(node);
2476 return true;
2477 }
2478 if (node instanceof AST_Scope) {
2479 return true;
2480 }
2481 }));
2482 }
2483
2484 function is_undefined(node, compressor) {
2485 return node.is_undefined
2486 || node instanceof AST_Undefined
2487 || node instanceof AST_UnaryPrefix
2488 && node.operator == "void"
2489 && !node.expression.has_side_effects(compressor);
2490 }
2491
2492 // is_truthy()
2493 // return true if `!!node === true`
2494 (function(def) {
2495 def(AST_Node, return_false);
2496 def(AST_Array, return_true);
2497 def(AST_Assign, function() {
2498 return this.operator == "=" && this.right.is_truthy();
2499 });
2500 def(AST_Lambda, return_true);
2501 def(AST_Object, return_true);
2502 def(AST_RegExp, return_true);
2503 def(AST_Sequence, function() {
2504 return this.tail_node().is_truthy();
2505 });
2506 def(AST_SymbolRef, function() {
2507 var fixed = this.fixed_value();
2508 if (!fixed) return false;
2509 this.is_truthy = return_false;
2510 var result = fixed.is_truthy();
2511 delete this.is_truthy;
2512 return result;
2513 });
2514 })(function(node, func) {
2515 node.DEFMETHOD("is_truthy", func);
2516 });
2517
2518 // is_negative_zero()
2519 // return true if the node may represent -0
2520 (function(def) {
2521 def(AST_Node, return_true);
2522 def(AST_Array, return_false);
2523 function binary(op, left, right) {
2524 switch (op) {
2525 case "-":
2526 return left.is_negative_zero()
2527 && (!(right instanceof AST_Constant) || right.value == 0);
2528 case "&&":
2529 case "||":
2530 return left.is_negative_zero() || right.is_negative_zero();
2531 case "*":
2532 case "/":
2533 case "%":
2534 return true;
2535 default:
2536 return false;
2537 }
2538 }
2539 def(AST_Assign, function() {
2540 var op = this.operator;
2541 if (op == "=") return this.right.is_negative_zero();
2542 return binary(op.slice(0, -1), this.left, this.right);
2543 });
2544 def(AST_Binary, function() {
2545 return binary(this.operator, this.left, this.right);
2546 });
2547 def(AST_Constant, function() {
2548 return this.value == 0 && 1 / this.value < 0;
2549 });
2550 def(AST_Lambda, return_false);
2551 def(AST_Object, return_false);
2552 def(AST_RegExp, return_false);
2553 def(AST_Sequence, function() {
2554 return this.tail_node().is_negative_zero();
2555 });
2556 def(AST_SymbolRef, function() {
2557 var fixed = this.fixed_value();
2558 if (!fixed) return true;
2559 this.is_negative_zero = return_true;
2560 var result = fixed.is_negative_zero();
2561 delete this.is_negative_zero;
2562 return result;
2563 });
2564 def(AST_UnaryPrefix, function() {
2565 return this.operator == "+" && this.expression.is_negative_zero()
2566 || this.operator == "-";
2567 });
2568 })(function(node, func) {
2569 node.DEFMETHOD("is_negative_zero", func);
2570 });
2571
2572 // may_throw_on_access()
2573 // returns true if this node may be null, undefined or contain `AST_Accessor`
2574 (function(def) {
2575 AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
2576 return !compressor.option("pure_getters") || this._dot_throw(compressor);
2577 });
2578 function is_strict(compressor) {
2579 return /strict/.test(compressor.option("pure_getters"));
2580 }
2581 def(AST_Node, is_strict);
2582 def(AST_Array, return_false);
2583 def(AST_Assign, function(compressor) {
2584 if (this.operator != "=") return false;
2585 var rhs = this.right;
2586 if (!rhs._dot_throw(compressor)) return false;
2587 var sym = this.left;
2588 if (!(sym instanceof AST_SymbolRef)) return true;
2589 if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
2590 return rhs.right._dot_throw(compressor);
2591 }
2592 return true;
2593 });
2594 def(AST_Binary, function(compressor) {
2595 switch (this.operator) {
2596 case "&&":
2597 return this.left._dot_throw(compressor) || this.right._dot_throw(compressor);
2598 case "||":
2599 return this.right._dot_throw(compressor);
2600 default:
2601 return false;
2602 }
2603 });
2604 def(AST_Conditional, function(compressor) {
2605 return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
2606 });
2607 def(AST_Constant, return_false);
2608 def(AST_Dot, function(compressor) {
2609 if (!is_strict(compressor)) return false;
2610 var exp = this.expression;
2611 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
2612 return !(exp instanceof AST_Lambda && this.property == "prototype");
2613 });
2614 def(AST_Lambda, return_false);
2615 def(AST_Null, return_true);
2616 def(AST_Object, function(compressor) {
2617 if (!is_strict(compressor)) return false;
2618 for (var i = this.properties.length; --i >=0;)
2619 if (this.properties[i].value instanceof AST_Accessor) return true;
2620 return false;
2621 });
2622 def(AST_Sequence, function(compressor) {
2623 return this.tail_node()._dot_throw(compressor);
2624 });
2625 def(AST_SymbolRef, function(compressor) {
2626 if (this.is_undefined) return true;
2627 if (!is_strict(compressor)) return false;
2628 if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
2629 if (this.is_immutable()) return false;
2630 if (is_arguments(this.definition())) return false;
2631 var fixed = this.fixed_value();
2632 if (!fixed) return true;
2633 this._dot_throw = return_true;
2634 var result = fixed._dot_throw(compressor);
2635 delete this._dot_throw;
2636 return result;
2637 });
2638 def(AST_UnaryPrefix, function() {
2639 return this.operator == "void";
2640 });
2641 def(AST_UnaryPostfix, return_false);
2642 def(AST_Undefined, return_true);
2643 })(function(node, func) {
2644 node.DEFMETHOD("_dot_throw", func);
2645 });
2646
2647 (function(def) {
2648 def(AST_Node, return_false);
2649 def(AST_Array, return_true);
2650 def(AST_Assign, function(compressor) {
2651 return this.operator != "=" || this.right.is_defined(compressor);
2652 });
2653 def(AST_Binary, function(compressor) {
2654 switch (this.operator) {
2655 case "&&":
2656 return this.left.is_defined(compressor) && this.right.is_defined(compressor);
2657 case "||":
2658 return this.left.is_truthy() || this.right.is_defined(compressor);
2659 default:
2660 return true;
2661 }
2662 });
2663 def(AST_Conditional, function(compressor) {
2664 return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
2665 });
2666 def(AST_Constant, return_true);
2667 def(AST_Lambda, return_true);
2668 def(AST_Object, return_true);
2669 def(AST_Sequence, function(compressor) {
2670 return this.tail_node().is_defined(compressor);
2671 });
2672 def(AST_SymbolRef, function(compressor) {
2673 if (this.is_undefined) return false;
2674 if (is_undeclared_ref(this) && this.is_declared(compressor)) return true;
2675 if (this.is_immutable()) return true;
2676 var fixed = this.fixed_value();
2677 if (!fixed) return false;
2678 this.is_defined = return_false;
2679 var result = fixed.is_defined(compressor);
2680 delete this.is_defined;
2681 return result;
2682 });
2683 def(AST_UnaryPrefix, function() {
2684 return this.operator != "void";
2685 });
2686 def(AST_UnaryPostfix, return_true);
2687 def(AST_Undefined, return_false);
2688 })(function(node, func) {
2689 node.DEFMETHOD("is_defined", func);
2690 });
2691
2692 /* -----[ boolean/negation helpers ]----- */
2693
2694 // methods to determine whether an expression has a boolean result type
2695 (function(def) {
2696 def(AST_Node, return_false);
2697 def(AST_Assign, function(compressor) {
2698 return this.operator == "=" && this.right.is_boolean(compressor);
2699 });
2700 var binary = makePredicate("in instanceof == != === !== < <= >= >");
2701 def(AST_Binary, function(compressor) {
2702 return binary[this.operator] || lazy_op[this.operator]
2703 && this.left.is_boolean(compressor)
2704 && this.right.is_boolean(compressor);
2705 });
2706 def(AST_Boolean, return_true);
2707 var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some");
2708 def(AST_Call, function(compressor) {
2709 if (!compressor.option("unsafe")) return false;
2710 var exp = this.expression;
2711 return exp instanceof AST_Dot && (fn[exp.property]
2712 || exp.property == "test" && exp.expression instanceof AST_RegExp);
2713 });
2714 def(AST_Conditional, function(compressor) {
2715 return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
2716 });
2717 def(AST_New, return_false);
2718 def(AST_Sequence, function(compressor) {
2719 return this.tail_node().is_boolean(compressor);
2720 });
2721 def(AST_SymbolRef, function(compressor) {
2722 var fixed = this.fixed_value();
2723 if (!fixed) return false;
2724 this.is_boolean = return_false;
2725 var result = fixed.is_boolean(compressor);
2726 delete this.is_boolean;
2727 return result;
2728 });
2729 var unary = makePredicate("! delete");
2730 def(AST_UnaryPrefix, function() {
2731 return unary[this.operator];
2732 });
2733 })(function(node, func) {
2734 node.DEFMETHOD("is_boolean", func);
2735 });
2736
2737 // methods to determine if an expression has a numeric result type
2738 (function(def) {
2739 def(AST_Node, return_false);
2740 var binary = makePredicate("- * / % & | ^ << >> >>>");
2741 def(AST_Assign, function(compressor) {
2742 return binary[this.operator.slice(0, -1)]
2743 || this.operator == "=" && this.right.is_number(compressor);
2744 });
2745 def(AST_Binary, function(compressor) {
2746 if (binary[this.operator]) return true;
2747 if (this.operator != "+") return false;
2748 return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
2749 && (this.right.is_boolean(compressor) || this.right.is_number(compressor));
2750 });
2751 var fn = makePredicate([
2752 "charCodeAt",
2753 "getDate",
2754 "getDay",
2755 "getFullYear",
2756 "getHours",
2757 "getMilliseconds",
2758 "getMinutes",
2759 "getMonth",
2760 "getSeconds",
2761 "getTime",
2762 "getTimezoneOffset",
2763 "getUTCDate",
2764 "getUTCDay",
2765 "getUTCFullYear",
2766 "getUTCHours",
2767 "getUTCMilliseconds",
2768 "getUTCMinutes",
2769 "getUTCMonth",
2770 "getUTCSeconds",
2771 "getYear",
2772 "indexOf",
2773 "lastIndexOf",
2774 "localeCompare",
2775 "push",
2776 "search",
2777 "setDate",
2778 "setFullYear",
2779 "setHours",
2780 "setMilliseconds",
2781 "setMinutes",
2782 "setMonth",
2783 "setSeconds",
2784 "setTime",
2785 "setUTCDate",
2786 "setUTCFullYear",
2787 "setUTCHours",
2788 "setUTCMilliseconds",
2789 "setUTCMinutes",
2790 "setUTCMonth",
2791 "setUTCSeconds",
2792 "setYear",
2793 "toExponential",
2794 "toFixed",
2795 "toPrecision",
2796 ]);
2797 def(AST_Call, function(compressor) {
2798 if (!compressor.option("unsafe")) return false;
2799 var exp = this.expression;
2800 return exp instanceof AST_Dot && (fn[exp.property]
2801 || is_undeclared_ref(exp.expression) && exp.expression.name == "Math");
2802 });
2803 def(AST_Conditional, function(compressor) {
2804 return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
2805 });
2806 def(AST_New, return_false);
2807 def(AST_Number, return_true);
2808 def(AST_Sequence, function(compressor) {
2809 return this.tail_node().is_number(compressor);
2810 });
2811 def(AST_SymbolRef, function(compressor) {
2812 var fixed = this.fixed_value();
2813 if (!fixed) return false;
2814 this.is_number = return_false;
2815 var result = fixed.is_number(compressor);
2816 delete this.is_number;
2817 return result;
2818 });
2819 var unary = makePredicate("+ - ~ ++ --");
2820 def(AST_Unary, function() {
2821 return unary[this.operator];
2822 });
2823 })(function(node, func) {
2824 node.DEFMETHOD("is_number", func);
2825 });
2826
2827 // methods to determine if an expression has a string result type
2828 (function(def) {
2829 def(AST_Node, return_false);
2830 def(AST_Assign, function(compressor) {
2831 return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
2832 });
2833 def(AST_Binary, function(compressor) {
2834 return this.operator == "+" &&
2835 (this.left.is_string(compressor) || this.right.is_string(compressor));
2836 });
2837 var fn = makePredicate([
2838 "charAt",
2839 "substr",
2840 "substring",
2841 "toLowerCase",
2842 "toString",
2843 "toUpperCase",
2844 "trim",
2845 ]);
2846 def(AST_Call, function(compressor) {
2847 if (!compressor.option("unsafe")) return false;
2848 var exp = this.expression;
2849 return exp instanceof AST_Dot && fn[exp.property];
2850 });
2851 def(AST_Conditional, function(compressor) {
2852 return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
2853 });
2854 def(AST_Sequence, function(compressor) {
2855 return this.tail_node().is_string(compressor);
2856 });
2857 def(AST_String, return_true);
2858 def(AST_SymbolRef, function(compressor) {
2859 var fixed = this.fixed_value();
2860 if (!fixed) return false;
2861 this.is_string = return_false;
2862 var result = fixed.is_string(compressor);
2863 delete this.is_string;
2864 return result;
2865 });
2866 def(AST_UnaryPrefix, function() {
2867 return this.operator == "typeof";
2868 });
2869 })(function(node, func) {
2870 node.DEFMETHOD("is_string", func);
2871 });
2872
2873 var lazy_op = makePredicate("&& ||");
2874 var unary_arithmetic = makePredicate("++ --");
2875 var unary_side_effects = makePredicate("delete ++ --");
2876
2877 function is_lhs(node, parent) {
2878 if (parent instanceof AST_Unary && unary_side_effects[parent.operator]) return parent.expression;
2879 if (parent instanceof AST_Assign && parent.left === node) return node;
2880 }
2881
2882 (function(def) {
2883 function to_node(value, orig) {
2884 if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
2885 if (Array.isArray(value)) return make_node(AST_Array, orig, {
2886 elements: value.map(function(value) {
2887 return to_node(value, orig);
2888 })
2889 });
2890 if (value && typeof value == "object") {
2891 var props = [];
2892 for (var key in value) if (HOP(value, key)) {
2893 props.push(make_node(AST_ObjectKeyVal, orig, {
2894 key: key,
2895 value: to_node(value[key], orig)
2896 }));
2897 }
2898 return make_node(AST_Object, orig, {
2899 properties: props
2900 });
2901 }
2902 return make_node_from_constant(value, orig);
2903 }
2904
2905 function warn(node) {
2906 AST_Node.warn("global_defs " + node.print_to_string() + " redefined [{file}:{line},{col}]", node.start);
2907 }
2908
2909 AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
2910 if (!compressor.option("global_defs")) return this;
2911 this.figure_out_scope({ ie8: compressor.option("ie8") });
2912 return this.transform(new TreeTransformer(function(node) {
2913 var def = node._find_defs(compressor, "");
2914 if (!def) return;
2915 var level = 0, child = node, parent;
2916 while (parent = this.parent(level++)) {
2917 if (!(parent instanceof AST_PropAccess)) break;
2918 if (parent.expression !== child) break;
2919 child = parent;
2920 }
2921 if (is_lhs(child, parent)) {
2922 warn(node);
2923 return;
2924 }
2925 return def;
2926 }));
2927 });
2928 def(AST_Node, noop);
2929 def(AST_Dot, function(compressor, suffix) {
2930 return this.expression._find_defs(compressor, "." + this.property + suffix);
2931 });
2932 def(AST_SymbolDeclaration, function(compressor) {
2933 if (!this.global()) return;
2934 if (HOP(compressor.option("global_defs"), this.name)) warn(this);
2935 });
2936 def(AST_SymbolRef, function(compressor, suffix) {
2937 if (!this.global()) return;
2938 var defines = compressor.option("global_defs");
2939 var name = this.name + suffix;
2940 if (HOP(defines, name)) return to_node(defines[name], this);
2941 });
2942 })(function(node, func) {
2943 node.DEFMETHOD("_find_defs", func);
2944 });
2945
2946 function best_of_expression(ast1, ast2, threshold) {
2947 var delta = ast2.print_to_string().length - ast1.print_to_string().length;
2948 return delta < (threshold || 0) ? ast2 : ast1;
2949 }
2950
2951 function best_of_statement(ast1, ast2, threshold) {
2952 return best_of_expression(make_node(AST_SimpleStatement, ast1, {
2953 body: ast1
2954 }), make_node(AST_SimpleStatement, ast2, {
2955 body: ast2
2956 }), threshold).body;
2957 }
2958
2959 function best_of(compressor, ast1, ast2, threshold) {
2960 return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
2961 }
2962
2963 function convert_to_predicate(obj) {
2964 var map = Object.create(null);
2965 Object.keys(obj).forEach(function(key) {
2966 map[key] = makePredicate(obj[key]);
2967 });
2968 return map;
2969 }
2970
2971 AST_Lambda.DEFMETHOD("first_statement", function() {
2972 var body = this.body;
2973 for (var i = 0; i < body.length; i++) {
2974 var stat = body[i];
2975 if (!(stat instanceof AST_Directive)) return stat;
2976 }
2977 });
2978
2979 function try_evaluate(compressor, node) {
2980 var ev = node.evaluate(compressor);
2981 if (ev === node) return node;
2982 ev = make_node_from_constant(ev, node).optimize(compressor);
2983 return best_of(compressor, node, ev, compressor.eval_threshold);
2984 }
2985
2986 var object_fns = [
2987 "constructor",
2988 "toString",
2989 "valueOf",
2990 ];
2991 var native_fns = convert_to_predicate({
2992 Array: [
2993 "indexOf",
2994 "join",
2995 "lastIndexOf",
2996 "slice",
2997 ].concat(object_fns),
2998 Boolean: object_fns,
2999 Function: object_fns,
3000 Number: [
3001 "toExponential",
3002 "toFixed",
3003 "toPrecision",
3004 ].concat(object_fns),
3005 Object: object_fns,
3006 RegExp: [
3007 "exec",
3008 "test",
3009 ].concat(object_fns),
3010 String: [
3011 "charAt",
3012 "charCodeAt",
3013 "concat",
3014 "indexOf",
3015 "italics",
3016 "lastIndexOf",
3017 "match",
3018 "replace",
3019 "search",
3020 "slice",
3021 "split",
3022 "substr",
3023 "substring",
3024 "toLowerCase",
3025 "toUpperCase",
3026 "trim",
3027 ].concat(object_fns),
3028 });
3029 var static_fns = convert_to_predicate({
3030 Array: [
3031 "isArray",
3032 ],
3033 Math: [
3034 "abs",
3035 "acos",
3036 "asin",
3037 "atan",
3038 "ceil",
3039 "cos",
3040 "exp",
3041 "floor",
3042 "log",
3043 "round",
3044 "sin",
3045 "sqrt",
3046 "tan",
3047 "atan2",
3048 "pow",
3049 "max",
3050 "min",
3051 ],
3052 Number: [
3053 "isFinite",
3054 "isNaN",
3055 ],
3056 Object: [
3057 "create",
3058 "getOwnPropertyDescriptor",
3059 "getOwnPropertyNames",
3060 "getPrototypeOf",
3061 "isExtensible",
3062 "isFrozen",
3063 "isSealed",
3064 "keys",
3065 ],
3066 String: [
3067 "fromCharCode",
3068 ],
3069 });
3070
3071 // methods to evaluate a constant expression
3072 (function(def) {
3073 // If the node has been successfully reduced to a constant,
3074 // then its value is returned; otherwise the element itself
3075 // is returned.
3076 //
3077 // They can be distinguished as constant value is never a
3078 // descendant of AST_Node.
3079 //
3080 // When `ignore_side_effects` is `true`, inspect the constant value
3081 // produced without worrying about any side effects caused by said
3082 // expression.
3083 AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
3084 if (!compressor.option("evaluate")) return this;
3085 var cached = [];
3086 var val = this._eval(compressor, ignore_side_effects, cached, 1);
3087 cached.forEach(function(node) {
3088 delete node._eval;
3089 });
3090 if (ignore_side_effects) return val;
3091 if (!val || val instanceof RegExp) return val;
3092 if (typeof val == "function" || typeof val == "object") return this;
3093 return val;
3094 });
3095 var unaryPrefix = makePredicate("! ~ - + void");
3096 AST_Node.DEFMETHOD("is_constant", function() {
3097 // Accomodate when compress option evaluate=false
3098 // as well as the common constant expressions !0 and -1
3099 if (this instanceof AST_Constant) {
3100 return !(this instanceof AST_RegExp);
3101 } else {
3102 return this instanceof AST_UnaryPrefix
3103 && this.expression instanceof AST_Constant
3104 && unaryPrefix[this.operator];
3105 }
3106 });
3107 def(AST_Statement, function() {
3108 throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
3109 });
3110 def(AST_Lambda, return_this);
3111 def(AST_Node, return_this);
3112 def(AST_Constant, function() {
3113 return this.value;
3114 });
3115 def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
3116 if (!ignore_side_effects) return this;
3117 if (this.operator != "=") return this;
3118 var node = this.right;
3119 var value = node._eval(compressor, ignore_side_effects, cached, depth);
3120 return value === node ? this : value;
3121 });
3122 def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
3123 if (!ignore_side_effects) return this;
3124 var node = this.tail_node();
3125 var value = node._eval(compressor, ignore_side_effects, cached, depth);
3126 return value === node ? this : value;
3127 });
3128 def(AST_Function, function(compressor) {
3129 if (compressor.option("unsafe")) {
3130 var fn = function() {};
3131 fn.node = this;
3132 fn.toString = function() {
3133 return "function(){}";
3134 };
3135 return fn;
3136 }
3137 return this;
3138 });
3139 def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
3140 if (compressor.option("unsafe")) {
3141 var elements = [];
3142 for (var i = 0; i < this.elements.length; i++) {
3143 var element = this.elements[i];
3144 var value = element._eval(compressor, ignore_side_effects, cached, depth);
3145 if (element === value) return this;
3146 elements.push(value);
3147 }
3148 return elements;
3149 }
3150 return this;
3151 });
3152 def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
3153 if (compressor.option("unsafe")) {
3154 var val = {};
3155 for (var i = 0; i < this.properties.length; i++) {
3156 var prop = this.properties[i];
3157 var key = prop.key;
3158 if (key instanceof AST_Symbol) {
3159 key = key.name;
3160 } else if (key instanceof AST_Node) {
3161 key = key._eval(compressor, ignore_side_effects, cached, depth);
3162 if (key === prop.key) return this;
3163 }
3164 if (typeof Object.prototype[key] === 'function') {
3165 return this;
3166 }
3167 if (prop.value instanceof AST_Function) continue;
3168 val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
3169 if (val[key] === prop.value) return this;
3170 }
3171 return val;
3172 }
3173 return this;
3174 });
3175 var non_converting_unary = makePredicate("! typeof void");
3176 def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
3177 var e = this.expression;
3178 // Function would be evaluated to an array and so typeof would
3179 // incorrectly return 'object'. Hence making is a special case.
3180 if (compressor.option("typeofs")
3181 && this.operator == "typeof"
3182 && (e instanceof AST_Lambda
3183 || e instanceof AST_SymbolRef
3184 && e.fixed_value() instanceof AST_Lambda)) {
3185 return typeof function(){};
3186 }
3187 if (!non_converting_unary[this.operator]) depth++;
3188 var v = e._eval(compressor, ignore_side_effects, cached, depth);
3189 if (v === this.expression) return this;
3190 switch (this.operator) {
3191 case "!": return !v;
3192 case "typeof":
3193 // typeof <RegExp> returns "object" or "function" on different platforms
3194 // so cannot evaluate reliably
3195 if (v instanceof RegExp) return this;
3196 return typeof v;
3197 case "void": return void v;
3198 case "~": return ~v;
3199 case "-": return -v;
3200 case "+": return +v;
3201 case "++":
3202 case "--":
3203 if (!(e instanceof AST_SymbolRef)) return this;
3204 var refs = e.definition().references;
3205 if (refs[refs.length - 1] !== e) return this;
3206 return HOP(e, "_eval") ? +(this.operator[0] + 1) + +v : v;
3207 }
3208 return this;
3209 });
3210 var non_converting_binary = makePredicate("&& || === !==");
3211 def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
3212 if (!non_converting_binary[this.operator]) depth++;
3213 var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
3214 if (left === this.left) return this;
3215 if (this.operator == (left ? "||" : "&&")) return left;
3216 var right = this.right._eval(compressor, ignore_side_effects, cached, depth);
3217 if (right === this.right) return this;
3218 var result;
3219 switch (this.operator) {
3220 case "&&" : result = left && right; break;
3221 case "||" : result = left || right; break;
3222 case "|" : result = left | right; break;
3223 case "&" : result = left & right; break;
3224 case "^" : result = left ^ right; break;
3225 case "+" : result = left + right; break;
3226 case "*" : result = left * right; break;
3227 case "/" : result = left / right; break;
3228 case "%" : result = left % right; break;
3229 case "-" : result = left - right; break;
3230 case "<<" : result = left << right; break;
3231 case ">>" : result = left >> right; break;
3232 case ">>>": result = left >>> right; break;
3233 case "==" : result = left == right; break;
3234 case "===": result = left === right; break;
3235 case "!=" : result = left != right; break;
3236 case "!==": result = left !== right; break;
3237 case "<" : result = left < right; break;
3238 case "<=" : result = left <= right; break;
3239 case ">" : result = left > right; break;
3240 case ">=" : result = left >= right; break;
3241 default : return this;
3242 }
3243 if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
3244 if (compressor.option("unsafe_math")
3245 && !ignore_side_effects
3246 && result
3247 && typeof result == "number"
3248 && (this.operator == "+" || this.operator == "-")) {
3249 var digits = Math.max(0, decimals(left), decimals(right));
3250 // 53-bit significand => 15.95 decimal places
3251 if (digits < 16) return +result.toFixed(digits);
3252 }
3253 return result;
3254
3255 function decimals(operand) {
3256 var match = /(\.[0-9]*)?(e.+)?$/.exec(+operand);
3257 return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
3258 }
3259 });
3260 def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
3261 var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
3262 if (condition === this.condition) return this;
3263 var node = condition ? this.consequent : this.alternative;
3264 var value = node._eval(compressor, ignore_side_effects, cached, depth);
3265 return value === node ? this : value;
3266 });
3267 def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
3268 var fixed = this.fixed_value();
3269 if (!fixed) return this;
3270 var value;
3271 if (member(fixed, cached)) {
3272 value = fixed._eval();
3273 } else {
3274 this._eval = return_this;
3275 value = fixed._eval(compressor, ignore_side_effects, cached, depth);
3276 delete this._eval;
3277 if (value === fixed) return this;
3278 fixed._eval = function() {
3279 return value;
3280 };
3281 cached.push(fixed);
3282 }
3283 if (value && typeof value == "object") {
3284 var escaped = this.definition().escaped;
3285 switch (escaped.length) {
3286 case 0:
3287 break;
3288 case 1:
3289 if (contains_ref(escaped[0], this)) break;
3290 default:
3291 if (depth > escaped.depth) return this;
3292 }
3293 }
3294 return value;
3295
3296 function contains_ref(expr, ref) {
3297 var found = false;
3298 expr.walk(new TreeWalker(function(node) {
3299 if (found) return true;
3300 if (node === ref) return found = true;
3301 }));
3302 return found;
3303 }
3304 });
3305 var global_objs = {
3306 Array: Array,
3307 Math: Math,
3308 Number: Number,
3309 Object: Object,
3310 String: String,
3311 };
3312 var static_values = convert_to_predicate({
3313 Math: [
3314 "E",
3315 "LN10",
3316 "LN2",
3317 "LOG2E",
3318 "LOG10E",
3319 "PI",
3320 "SQRT1_2",
3321 "SQRT2",
3322 ],
3323 Number: [
3324 "MAX_VALUE",
3325 "MIN_VALUE",
3326 "NaN",
3327 "NEGATIVE_INFINITY",
3328 "POSITIVE_INFINITY",
3329 ],
3330 });
3331 var regexp_props = makePredicate("global ignoreCase multiline source");
3332 def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
3333 if (compressor.option("unsafe")) {
3334 var key = this.property;
3335 if (key instanceof AST_Node) {
3336 key = key._eval(compressor, ignore_side_effects, cached, depth);
3337 if (key === this.property) return this;
3338 }
3339 var exp = this.expression;
3340 var val;
3341 if (is_undeclared_ref(exp)) {
3342 var static_value = static_values[exp.name];
3343 if (!static_value || !static_value[key]) return this;
3344 val = global_objs[exp.name];
3345 } else {
3346 val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
3347 if (val == null || val === exp) return this;
3348 if (val instanceof RegExp) {
3349 if (!regexp_props[key]) return this;
3350 } else if (typeof val == "object") {
3351 if (!HOP(val, key)) return this;
3352 } else if (typeof val == "function") switch (key) {
3353 case "name":
3354 return val.node.name ? val.node.name.name : "";
3355 case "length":
3356 return val.node.argnames.length;
3357 default:
3358 return this;
3359 }
3360 }
3361 return val[key];
3362 }
3363 return this;
3364 });
3365 def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
3366 var exp = this.expression;
3367 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
3368 if (fn instanceof AST_Lambda) {
3369 if (fn.evaluating) return this;
3370 if (fn.name && fn.name.definition().recursive_refs > 0) return this;
3371 var stat = fn.first_statement();
3372 if (!(stat instanceof AST_Return)) return this;
3373 var args = eval_args(this.args);
3374 if (!args) return this;
3375 if (!stat.value) return undefined;
3376 if (!all(fn.argnames, function(sym, i) {
3377 var value = args[i];
3378 var def = sym.definition();
3379 if (def.orig[def.orig.length - 1] !== sym) return false;
3380 def.references.forEach(function(node) {
3381 node._eval = function() {
3382 return value;
3383 };
3384 cached.push(node);
3385 });
3386 return true;
3387 })) return this;
3388 fn.evaluating = true;
3389 var val = stat.value._eval(compressor, ignore_side_effects, cached, depth);
3390 delete fn.evaluating;
3391 if (val === stat.value) return this;
3392 return val;
3393 } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
3394 var key = exp.property;
3395 if (key instanceof AST_Node) {
3396 key = key._eval(compressor, ignore_side_effects, cached, depth);
3397 if (key === exp.property) return this;
3398 }
3399 var val;
3400 var e = exp.expression;
3401 if (is_undeclared_ref(e)) {
3402 var static_fn = static_fns[e.name];
3403 if (!static_fn || !static_fn[key]) return this;
3404 val = global_objs[e.name];
3405 } else {
3406 val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
3407 if (val == null || val === e) return this;
3408 var native_fn = native_fns[val.constructor.name];
3409 if (!native_fn || !native_fn[key]) return this;
3410 if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
3411 }
3412 var args = eval_args(this.args);
3413 if (!args) return this;
3414 if (key == "replace" && typeof args[1] == "function") return this;
3415 try {
3416 return val[key].apply(val, args);
3417 } catch (ex) {
3418 AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
3419 code: this.print_to_string(),
3420 file: this.start.file,
3421 line: this.start.line,
3422 col: this.start.col
3423 });
3424 } finally {
3425 if (val instanceof RegExp) val.lastIndex = 0;
3426 }
3427 }
3428 return this;
3429
3430 function eval_args(args) {
3431 var values = [];
3432 for (var i = 0; i < args.length; i++) {
3433 var arg = args[i];
3434 var value = arg._eval(compressor, ignore_side_effects, cached, depth);
3435 if (arg === value) return;
3436 values.push(value);
3437 }
3438 return values;
3439 }
3440 });
3441 def(AST_New, return_this);
3442 })(function(node, func) {
3443 node.DEFMETHOD("_eval", func);
3444 });
3445
3446 // method to negate an expression
3447 (function(def) {
3448 function basic_negation(exp) {
3449 return make_node(AST_UnaryPrefix, exp, {
3450 operator: "!",
3451 expression: exp
3452 });
3453 }
3454 function best(orig, alt, first_in_statement) {
3455 var negated = basic_negation(orig);
3456 if (first_in_statement) {
3457 var stat = make_node(AST_SimpleStatement, alt, {
3458 body: alt
3459 });
3460 return best_of_expression(negated, stat) === stat ? alt : negated;
3461 }
3462 return best_of_expression(negated, alt);
3463 }
3464 def(AST_Node, function() {
3465 return basic_negation(this);
3466 });
3467 def(AST_Statement, function() {
3468 throw new Error("Cannot negate a statement");
3469 });
3470 def(AST_Function, function() {
3471 return basic_negation(this);
3472 });
3473 def(AST_UnaryPrefix, function() {
3474 if (this.operator == "!")
3475 return this.expression;
3476 return basic_negation(this);
3477 });
3478 def(AST_Sequence, function(compressor) {
3479 var expressions = this.expressions.slice();
3480 expressions.push(expressions.pop().negate(compressor));
3481 return make_sequence(this, expressions);
3482 });
3483 def(AST_Conditional, function(compressor, first_in_statement) {
3484 var self = this.clone();
3485 self.consequent = self.consequent.negate(compressor);
3486 self.alternative = self.alternative.negate(compressor);
3487 return best(this, self, first_in_statement);
3488 });
3489 def(AST_Binary, function(compressor, first_in_statement) {
3490 var self = this.clone(), op = this.operator;
3491 if (compressor.option("unsafe_comps")) {
3492 switch (op) {
3493 case "<=" : self.operator = ">" ; return self;
3494 case "<" : self.operator = ">=" ; return self;
3495 case ">=" : self.operator = "<" ; return self;
3496 case ">" : self.operator = "<=" ; return self;
3497 }
3498 }
3499 switch (op) {
3500 case "==" : self.operator = "!="; return self;
3501 case "!=" : self.operator = "=="; return self;
3502 case "===": self.operator = "!=="; return self;
3503 case "!==": self.operator = "==="; return self;
3504 case "&&":
3505 self.operator = "||";
3506 self.left = self.left.negate(compressor, first_in_statement);
3507 self.right = self.right.negate(compressor);
3508 return best(this, self, first_in_statement);
3509 case "||":
3510 self.operator = "&&";
3511 self.left = self.left.negate(compressor, first_in_statement);
3512 self.right = self.right.negate(compressor);
3513 return best(this, self, first_in_statement);
3514 }
3515 return basic_negation(this);
3516 });
3517 })(function(node, func) {
3518 node.DEFMETHOD("negate", function(compressor, first_in_statement) {
3519 return func.call(this, compressor, first_in_statement);
3520 });
3521 });
3522
3523 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");
3524 AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
3525 if (compressor.option("unsafe")) {
3526 var expr = this.expression;
3527 if (is_undeclared_ref(expr) && global_pure_fns[expr.name]) return true;
3528 if (expr instanceof AST_Dot && is_undeclared_ref(expr.expression)) {
3529 var static_fn = static_fns[expr.expression.name];
3530 return static_fn && static_fn[expr.property];
3531 }
3532 }
3533 return this.pure || !compressor.pure_funcs(this);
3534 });
3535 AST_Node.DEFMETHOD("is_call_pure", return_false);
3536 AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
3537 if (!compressor.option("unsafe")) return false;
3538 var dot = this.expression;
3539 if (!(dot instanceof AST_Dot)) return false;
3540 var exp = dot.expression;
3541 var map;
3542 var prop = dot.property;
3543 if (exp instanceof AST_Array) {
3544 map = native_fns.Array;
3545 } else if (exp.is_boolean(compressor)) {
3546 map = native_fns.Boolean;
3547 } else if (exp.is_number(compressor)) {
3548 map = native_fns.Number;
3549 } else if (exp instanceof AST_RegExp) {
3550 map = native_fns.RegExp;
3551 } else if (exp.is_string(compressor)) {
3552 map = native_fns.String;
3553 if (prop == "replace") {
3554 var arg = this.args[1];
3555 if (arg && !arg.is_string(compressor)) return false;
3556 }
3557 } else if (!dot.may_throw_on_access(compressor)) {
3558 map = native_fns.Object;
3559 }
3560 return map && map[prop];
3561 });
3562
3563 // determine if expression has side effects
3564 (function(def) {
3565 function any(list, compressor) {
3566 for (var i = list.length; --i >= 0;)
3567 if (list[i].has_side_effects(compressor))
3568 return true;
3569 return false;
3570 }
3571 def(AST_Node, return_true);
3572 def(AST_Array, function(compressor) {
3573 return any(this.elements, compressor);
3574 });
3575 def(AST_Assign, return_true);
3576 def(AST_Binary, function(compressor) {
3577 return this.left.has_side_effects(compressor)
3578 || this.right.has_side_effects(compressor);
3579 });
3580 def(AST_Block, function(compressor) {
3581 return any(this.body, compressor);
3582 });
3583 def(AST_Call, function(compressor) {
3584 if (!this.is_expr_pure(compressor)
3585 && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) {
3586 return true;
3587 }
3588 return any(this.args, compressor);
3589 });
3590 def(AST_Case, function(compressor) {
3591 return this.expression.has_side_effects(compressor)
3592 || any(this.body, compressor);
3593 });
3594 def(AST_Conditional, function(compressor) {
3595 return this.condition.has_side_effects(compressor)
3596 || this.consequent.has_side_effects(compressor)
3597 || this.alternative.has_side_effects(compressor);
3598 });
3599 def(AST_Constant, return_false);
3600 def(AST_Definitions, function(compressor) {
3601 return any(this.definitions, compressor);
3602 });
3603 def(AST_Dot, function(compressor) {
3604 return this.expression.may_throw_on_access(compressor)
3605 || this.expression.has_side_effects(compressor);
3606 });
3607 def(AST_EmptyStatement, return_false);
3608 def(AST_If, function(compressor) {
3609 return this.condition.has_side_effects(compressor)
3610 || this.body && this.body.has_side_effects(compressor)
3611 || this.alternative && this.alternative.has_side_effects(compressor);
3612 });
3613 def(AST_LabeledStatement, function(compressor) {
3614 return this.body.has_side_effects(compressor);
3615 });
3616 def(AST_Lambda, return_false);
3617 def(AST_Object, function(compressor) {
3618 return any(this.properties, compressor);
3619 });
3620 def(AST_ObjectProperty, function(compressor) {
3621 return this.value.has_side_effects(compressor);
3622 });
3623 def(AST_Sub, function(compressor) {
3624 return this.expression.may_throw_on_access(compressor)
3625 || this.expression.has_side_effects(compressor)
3626 || this.property.has_side_effects(compressor);
3627 });
3628 def(AST_Sequence, function(compressor) {
3629 return any(this.expressions, compressor);
3630 });
3631 def(AST_SimpleStatement, function(compressor) {
3632 return this.body.has_side_effects(compressor);
3633 });
3634 def(AST_Switch, function(compressor) {
3635 return this.expression.has_side_effects(compressor)
3636 || any(this.body, compressor);
3637 });
3638 def(AST_SymbolDeclaration, return_false);
3639 def(AST_SymbolRef, function(compressor) {
3640 return !this.is_declared(compressor);
3641 });
3642 def(AST_This, return_false);
3643 def(AST_Try, function(compressor) {
3644 return any(this.body, compressor)
3645 || this.bcatch && this.bcatch.has_side_effects(compressor)
3646 || this.bfinally && this.bfinally.has_side_effects(compressor);
3647 });
3648 def(AST_Unary, function(compressor) {
3649 return unary_side_effects[this.operator]
3650 || this.expression.has_side_effects(compressor);
3651 });
3652 def(AST_VarDef, function(compressor) {
3653 return this.value;
3654 });
3655 })(function(node, func) {
3656 node.DEFMETHOD("has_side_effects", func);
3657 });
3658
3659 // determine if expression may throw
3660 (function(def) {
3661 def(AST_Node, return_true);
3662
3663 def(AST_Constant, return_false);
3664 def(AST_EmptyStatement, return_false);
3665 def(AST_Lambda, return_false);
3666 def(AST_SymbolDeclaration, return_false);
3667 def(AST_This, return_false);
3668
3669 function any(list, compressor) {
3670 for (var i = list.length; --i >= 0;)
3671 if (list[i].may_throw(compressor))
3672 return true;
3673 return false;
3674 }
3675
3676 def(AST_Array, function(compressor) {
3677 return any(this.elements, compressor);
3678 });
3679 def(AST_Assign, function(compressor) {
3680 if (this.right.may_throw(compressor)) return true;
3681 if (!compressor.has_directive("use strict")
3682 && this.operator == "="
3683 && this.left instanceof AST_SymbolRef) {
3684 return false;
3685 }
3686 return this.left.may_throw(compressor);
3687 });
3688 def(AST_Binary, function(compressor) {
3689 return this.left.may_throw(compressor)
3690 || this.right.may_throw(compressor);
3691 });
3692 def(AST_Block, function(compressor) {
3693 return any(this.body, compressor);
3694 });
3695 def(AST_Call, function(compressor) {
3696 if (any(this.args, compressor)) return true;
3697 if (this.is_expr_pure(compressor)) return false;
3698 if (this.expression.may_throw(compressor)) return true;
3699 return !(this.expression instanceof AST_Lambda)
3700 || any(this.expression.body, compressor);
3701 });
3702 def(AST_Case, function(compressor) {
3703 return this.expression.may_throw(compressor)
3704 || any(this.body, compressor);
3705 });
3706 def(AST_Conditional, function(compressor) {
3707 return this.condition.may_throw(compressor)
3708 || this.consequent.may_throw(compressor)
3709 || this.alternative.may_throw(compressor);
3710 });
3711 def(AST_Definitions, function(compressor) {
3712 return any(this.definitions, compressor);
3713 });
3714 def(AST_Dot, function(compressor) {
3715 return this.expression.may_throw_on_access(compressor)
3716 || this.expression.may_throw(compressor);
3717 });
3718 def(AST_If, function(compressor) {
3719 return this.condition.may_throw(compressor)
3720 || this.body && this.body.may_throw(compressor)
3721 || this.alternative && this.alternative.may_throw(compressor);
3722 });
3723 def(AST_LabeledStatement, function(compressor) {
3724 return this.body.may_throw(compressor);
3725 });
3726 def(AST_Object, function(compressor) {
3727 return any(this.properties, compressor);
3728 });
3729 def(AST_ObjectProperty, function(compressor) {
3730 return this.value.may_throw(compressor);
3731 });
3732 def(AST_Return, function(compressor) {
3733 return this.value && this.value.may_throw(compressor);
3734 });
3735 def(AST_Sequence, function(compressor) {
3736 return any(this.expressions, compressor);
3737 });
3738 def(AST_SimpleStatement, function(compressor) {
3739 return this.body.may_throw(compressor);
3740 });
3741 def(AST_Sub, function(compressor) {
3742 return this.expression.may_throw_on_access(compressor)
3743 || this.expression.may_throw(compressor)
3744 || this.property.may_throw(compressor);
3745 });
3746 def(AST_Switch, function(compressor) {
3747 return this.expression.may_throw(compressor)
3748 || any(this.body, compressor);
3749 });
3750 def(AST_SymbolRef, function(compressor) {
3751 return !this.is_declared(compressor);
3752 });
3753 def(AST_Try, function(compressor) {
3754 return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor))
3755 || this.bfinally && this.bfinally.may_throw(compressor);
3756 });
3757 def(AST_Unary, function(compressor) {
3758 if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef)
3759 return false;
3760 return this.expression.may_throw(compressor);
3761 });
3762 def(AST_VarDef, function(compressor) {
3763 if (!this.value) return false;
3764 return this.value.may_throw(compressor);
3765 });
3766 })(function(node, func) {
3767 node.DEFMETHOD("may_throw", func);
3768 });
3769
3770 // determine if expression is constant
3771 (function(def) {
3772 function all(list) {
3773 for (var i = list.length; --i >= 0;)
3774 if (!list[i].is_constant_expression())
3775 return false;
3776 return true;
3777 }
3778 def(AST_Node, return_false);
3779 def(AST_Array, function() {
3780 return all(this.elements);
3781 });
3782 def(AST_Binary, function() {
3783 return this.left.is_constant_expression() && this.right.is_constant_expression();
3784 });
3785 def(AST_Constant, return_true);
3786 def(AST_Lambda, function(scope) {
3787 var self = this;
3788 var result = true;
3789 var scopes = [];
3790 self.walk(new TreeWalker(function(node, descend) {
3791 if (!result) return true;
3792 if (node instanceof AST_Catch) {
3793 scopes.push(node.argname.scope);
3794 descend();
3795 scopes.pop();
3796 return true;
3797 }
3798 if (node instanceof AST_Scope) {
3799 if (node === self) return;
3800 scopes.push(node);
3801 descend();
3802 scopes.pop();
3803 return true;
3804 }
3805 if (node instanceof AST_SymbolRef) {
3806 if (self.inlined) {
3807 result = false;
3808 return true;
3809 }
3810 if (self.variables.has(node.name)) return true;
3811 var def = node.definition();
3812 if (member(def.scope, scopes)) return true;
3813 if (scope) {
3814 var scope_def = scope.find_variable(node);
3815 if (def.undeclared ? !scope_def : scope_def === def) {
3816 result = "f";
3817 return true;
3818 }
3819 }
3820 result = false;
3821 return true;
3822 }
3823 }));
3824 return result;
3825 });
3826 def(AST_Object, function() {
3827 return all(this.properties);
3828 });
3829 def(AST_ObjectProperty, function() {
3830 return this.value.is_constant_expression();
3831 });
3832 def(AST_Unary, function() {
3833 return this.expression.is_constant_expression();
3834 });
3835 })(function(node, func) {
3836 node.DEFMETHOD("is_constant_expression", func);
3837 });
3838
3839 // tell me if a statement aborts
3840 function aborts(thing) {
3841 return thing && thing.aborts();
3842 }
3843 (function(def) {
3844 def(AST_Statement, return_null);
3845 def(AST_Jump, return_this);
3846 function block_aborts() {
3847 var n = this.body.length;
3848 return n > 0 && aborts(this.body[n - 1]);
3849 }
3850 def(AST_BlockStatement, block_aborts);
3851 def(AST_SwitchBranch, block_aborts);
3852 def(AST_If, function() {
3853 return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
3854 });
3855 })(function(node, func) {
3856 node.DEFMETHOD("aborts", func);
3857 });
3858
3859 /* -----[ optimizers ]----- */
3860
3861 var directives = makePredicate(["use asm", "use strict"]);
3862 OPT(AST_Directive, function(self, compressor) {
3863 if (compressor.option("directives")
3864 && (!directives[self.value] || compressor.has_directive(self.value) !== self)) {
3865 return make_node(AST_EmptyStatement, self);
3866 }
3867 return self;
3868 });
3869
3870 OPT(AST_Debugger, function(self, compressor) {
3871 if (compressor.option("drop_debugger"))
3872 return make_node(AST_EmptyStatement, self);
3873 return self;
3874 });
3875
3876 OPT(AST_LabeledStatement, function(self, compressor) {
3877 if (self.body instanceof AST_Break
3878 && compressor.loopcontrol_target(self.body) === self.body) {
3879 return make_node(AST_EmptyStatement, self);
3880 }
3881 return self.label.references.length == 0 ? self.body : self;
3882 });
3883
3884 OPT(AST_Block, function(self, compressor) {
3885 self.body = tighten_body(self.body, compressor);
3886 return self;
3887 });
3888
3889 OPT(AST_BlockStatement, function(self, compressor) {
3890 self.body = tighten_body(self.body, compressor);
3891 switch (self.body.length) {
3892 case 1: return self.body[0];
3893 case 0: return make_node(AST_EmptyStatement, self);
3894 }
3895 return self;
3896 });
3897
3898 OPT(AST_Lambda, function(self, compressor) {
3899 self.body = tighten_body(self.body, compressor);
3900 if (compressor.option("side_effects")
3901 && self.body.length == 1
3902 && self.body[0] === compressor.has_directive("use strict")) {
3903 self.body.length = 0;
3904 }
3905 return self;
3906 });
3907
3908 AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
3909 if (!compressor.option("unused")) return;
3910 if (compressor.has_directive("use asm")) return;
3911 var self = this;
3912 if (self.pinned()) return;
3913 var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
3914 var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
3915 var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
3916 var sym;
3917 if (node instanceof AST_Assign) {
3918 if (node.write_only || node.operator == "=") sym = node.left;
3919 } else if (node instanceof AST_Unary) {
3920 if (node.write_only) sym = node.expression;
3921 }
3922 if (/strict/.test(compressor.option("pure_getters"))) {
3923 while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) {
3924 if (sym instanceof AST_Sub) props.unshift(sym.property);
3925 sym = sym.expression;
3926 }
3927 }
3928 if (!(sym instanceof AST_SymbolRef)) return;
3929 if (compressor.exposed(sym.definition())) return;
3930 if (!all(sym.definition().orig, function(sym) {
3931 return !(sym instanceof AST_SymbolLambda);
3932 })) return;
3933 return sym;
3934 };
3935 var in_use = [];
3936 var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
3937 var fixed_ids = Object.create(null);
3938 var value_read = Object.create(null);
3939 var value_modified = Object.create(null);
3940 if (self instanceof AST_Toplevel && compressor.top_retain) {
3941 self.variables.each(function(def) {
3942 if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
3943 in_use_ids[def.id] = true;
3944 in_use.push(def);
3945 }
3946 });
3947 }
3948 var var_defs_by_id = new Dictionary();
3949 var initializations = new Dictionary();
3950 // pass 1: find out which symbols are directly used in
3951 // this scope (not in nested scopes).
3952 var scope = this;
3953 var tw = new TreeWalker(function(node, descend) {
3954 if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) {
3955 node.argnames.forEach(function(argname) {
3956 var def = argname.definition();
3957 if (!(def.id in in_use_ids)) {
3958 in_use_ids[def.id] = true;
3959 in_use.push(def);
3960 }
3961 });
3962 }
3963 if (node === self) return;
3964 if (node instanceof AST_Defun) {
3965 var node_def = node.name.definition();
3966 if (!drop_funcs && scope === self) {
3967 if (!(node_def.id in in_use_ids)) {
3968 in_use_ids[node_def.id] = true;
3969 in_use.push(node_def);
3970 }
3971 }
3972 initializations.add(node_def.id, node);
3973 return true; // don't go in nested scopes
3974 }
3975 if (node instanceof AST_SymbolFunarg && scope === self) {
3976 var_defs_by_id.add(node.definition().id, node);
3977 }
3978 if (node instanceof AST_Definitions && scope === self) {
3979 node.definitions.forEach(function(def) {
3980 var node_def = def.name.definition();
3981 var_defs_by_id.add(node_def.id, def);
3982 if (!drop_vars) {
3983 if (!(node_def.id in in_use_ids)) {
3984 in_use_ids[node_def.id] = true;
3985 in_use.push(node_def);
3986 }
3987 }
3988 if (def.value) {
3989 initializations.add(node_def.id, def.value);
3990 if (def.value.has_side_effects(compressor)) {
3991 def.value.walk(tw);
3992 }
3993 if (!node_def.chained && def.name.fixed_value(true) === def.value) {
3994 fixed_ids[node_def.id] = def;
3995 }
3996 }
3997 });
3998 return true;
3999 }
4000 return scan_ref_scoped(node, descend, true);
4001 });
4002 self.walk(tw);
4003 // pass 2: for every used symbol we need to walk its
4004 // initialization code to figure out if it uses other
4005 // symbols (that may not be in_use).
4006 tw = new TreeWalker(scan_ref_scoped);
4007 for (var i = 0; i < in_use.length; i++) {
4008 var init = initializations.get(in_use[i].id);
4009 if (init) init.forEach(function(init) {
4010 init.walk(tw);
4011 });
4012 }
4013 var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie8") ? function(def) {
4014 return !compressor.exposed(def) && !def.references.length;
4015 } : function(def) {
4016 // any declarations with same name will overshadow
4017 // name of this anonymous function and can therefore
4018 // never be used anywhere
4019 return !(def.id in in_use_ids) || def.orig.length > 1;
4020 };
4021 // pass 3: we should drop declarations not in_use
4022 var unused_fn_names = [];
4023 var calls_to_drop_args = [];
4024 var fns_with_marked_args = [];
4025 var tt = new TreeTransformer(function(node, descend, in_list) {
4026 var parent = tt.parent();
4027 if (drop_vars) {
4028 var props = [], sym = assign_as_unused(node, props);
4029 if (sym) {
4030 var def = sym.definition();
4031 var in_use = def.id in in_use_ids;
4032 var value;
4033 if (node instanceof AST_Assign) {
4034 if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
4035 value = get_rhs(node);
4036 if (node.write_only === true) {
4037 value = value.drop_side_effect_free(compressor) || make_node(AST_Number, node, {
4038 value: 0
4039 });
4040 }
4041 }
4042 } else if (!in_use) {
4043 value = make_node(AST_Number, node, {
4044 value: 0
4045 });
4046 }
4047 if (value) {
4048 if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
4049 value = value.drop_side_effect_free(compressor);
4050 }
4051 if (value) props.push(value);
4052 switch (props.length) {
4053 case 0:
4054 return List.skip;
4055 case 1:
4056 return maintain_this_binding(compressor, parent, node, props[0].transform(tt));
4057 default:
4058 return make_sequence(node, props.map(function(prop) {
4059 return prop.transform(tt);
4060 }));
4061 }
4062 }
4063 }
4064 }
4065 if (node instanceof AST_Call) calls_to_drop_args.push(node);
4066 if (scope !== self) return;
4067 if (node instanceof AST_Function && node.name && drop_fn_name(node.name.definition())) {
4068 unused_fn_names.push(node);
4069 }
4070 if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
4071 var trim = compressor.drop_fargs(node, parent);
4072 for (var a = node.argnames, i = a.length; --i >= 0;) {
4073 var sym = a[i];
4074 if (!(sym.definition().id in in_use_ids)) {
4075 sym.__unused = true;
4076 if (trim) {
4077 log(sym, "Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
4078 a.pop();
4079 }
4080 } else {
4081 trim = false;
4082 }
4083 }
4084 fns_with_marked_args.push(node);
4085 }
4086 if (drop_funcs && node instanceof AST_Defun && node !== self) {
4087 var def = node.name.definition();
4088 if (!(def.id in in_use_ids)) {
4089 log(node.name, "Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
4090 def.eliminated++;
4091 return make_node(AST_EmptyStatement, node);
4092 }
4093 }
4094 if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
4095 // place uninitialized names at the start
4096 var body = [], head = [], tail = [];
4097 // for unused names whose initialization has
4098 // side effects, we can cascade the init. code
4099 // into the next one, or next statement.
4100 var side_effects = [];
4101 node.definitions.forEach(function(def) {
4102 if (def.value) def.value = def.value.transform(tt);
4103 var sym = def.name.definition();
4104 if (!drop_vars || sym.id in in_use_ids) {
4105 if (def.value && sym.id in fixed_ids && fixed_ids[sym.id] !== def) {
4106 def.value = def.value.drop_side_effect_free(compressor);
4107 }
4108 var var_defs = var_defs_by_id.get(sym.id);
4109 if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
4110 AST_Node.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
4111 if (def.value) {
4112 var ref = make_node(AST_SymbolRef, def.name, def.name);
4113 sym.references.push(ref);
4114 var assign = make_node(AST_Assign, def, {
4115 operator: "=",
4116 left: ref,
4117 right: def.value
4118 });
4119 if (fixed_ids[sym.id] === def) {
4120 fixed_ids[sym.id] = assign;
4121 }
4122 side_effects.push(assign.transform(tt));
4123 }
4124 remove(var_defs, def);
4125 sym.eliminated++;
4126 return;
4127 }
4128 if (!def.value) {
4129 head.push(def);
4130 } else if (compressor.option("functions")
4131 && !compressor.option("ie8")
4132 && def.value === def.name.fixed_value()
4133 && def.value instanceof AST_Function
4134 && !(def.value.name && def.value.name.definition().assignments)
4135 && can_rename(def.value, def.name.name)
4136 && (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) {
4137 AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
4138 var defun = make_node(AST_Defun, def, def.value);
4139 defun.name = make_node(AST_SymbolDefun, def.name, def.name);
4140 var name_def = def.name.scope.resolve().def_function(defun.name);
4141 if (def.value.name) {
4142 var old_def = def.value.name.definition();
4143 def.value.walk(new TreeWalker(function(node) {
4144 if (node instanceof AST_SymbolRef && node.definition() === old_def) {
4145 node.name = name_def.name;
4146 node.thedef = name_def;
4147 node.reference({});
4148 }
4149 }));
4150 }
4151 body.push(defun);
4152 } else {
4153 if (side_effects.length > 0) {
4154 if (tail.length > 0) {
4155 side_effects.push(def.value);
4156 def.value = make_sequence(def.value, side_effects);
4157 } else {
4158 body.push(make_node(AST_SimpleStatement, node, {
4159 body: make_sequence(node, side_effects)
4160 }));
4161 }
4162 side_effects = [];
4163 }
4164 tail.push(def);
4165 }
4166 } else if (sym.orig[0] instanceof AST_SymbolCatch) {
4167 var value = def.value && def.value.drop_side_effect_free(compressor);
4168 if (value) side_effects.push(value);
4169 def.value = null;
4170 head.push(def);
4171 } else {
4172 var value = def.value && !def.value.single_use && def.value.drop_side_effect_free(compressor);
4173 if (value) {
4174 AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
4175 side_effects.push(value);
4176 } else {
4177 log(def.name, "Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
4178 }
4179 sym.eliminated++;
4180 }
4181
4182 function can_rename(fn, name) {
4183 var def = fn.variables.get(name);
4184 return !def || fn.name && def === fn.name.definition();
4185 }
4186 });
4187 if (head.length > 0 || tail.length > 0) {
4188 node.definitions = head.concat(tail);
4189 body.push(node);
4190 }
4191 if (side_effects.length > 0) {
4192 body.push(make_node(AST_SimpleStatement, node, {
4193 body: make_sequence(node, side_effects)
4194 }));
4195 }
4196 switch (body.length) {
4197 case 0:
4198 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
4199 case 1:
4200 return body[0];
4201 default:
4202 return in_list ? List.splice(body) : make_node(AST_BlockStatement, node, {
4203 body: body
4204 });
4205 }
4206 }
4207 if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
4208 // Certain combination of unused name + side effect leads to invalid AST:
4209 // https://github.com/mishoo/UglifyJS2/issues/1830
4210 // We fix it at this stage by moving the label inwards, back to the `for`.
4211 descend(node, this);
4212 if (node.body instanceof AST_BlockStatement) {
4213 var block = node.body;
4214 node.body = block.body.pop();
4215 block.body.push(node);
4216 return in_list ? List.splice(block.body) : block;
4217 }
4218 return node;
4219 }
4220 if (node instanceof AST_Scope) {
4221 var save_scope = scope;
4222 scope = node;
4223 descend(node, this);
4224 scope = save_scope;
4225 return node;
4226 }
4227 }, function(node, in_list) {
4228 if (node instanceof AST_For) {
4229 // Certain combination of unused name + side effect leads to invalid AST:
4230 // https://github.com/mishoo/UglifyJS2/issues/44
4231 // https://github.com/mishoo/UglifyJS2/issues/1838
4232 // https://github.com/mishoo/UglifyJS2/issues/3371
4233 // We fix it at this stage by moving the `var` outside the `for`.
4234 var block;
4235 if (node.init instanceof AST_BlockStatement) {
4236 block = node.init;
4237 node.init = block.body.pop();
4238 block.body.push(node);
4239 }
4240 if (node.init instanceof AST_Defun) {
4241 if (!block) {
4242 block = make_node(AST_BlockStatement, node, {
4243 body: [ node ]
4244 });
4245 }
4246 block.body.splice(-1, 0, node.init);
4247 node.init = null;
4248 } else if (node.init instanceof AST_SimpleStatement) {
4249 node.init = node.init.body;
4250 } else if (is_empty(node.init)) {
4251 node.init = null;
4252 }
4253 return !block ? node : in_list ? List.splice(block.body) : block;
4254 } else if (node instanceof AST_ForIn) {
4255 if (!drop_vars || !compressor.option("loops")) return;
4256 if (!(node.init instanceof AST_Definitions)) return;
4257 var sym = node.init.definitions[0].name;
4258 if (sym.definition().id in in_use_ids) return;
4259 if (!is_empty(node.body)) return;
4260 log(sym, "Dropping unused loop variable {name} [{file}:{line},{col}]", template(sym));
4261 var value = node.object.drop_side_effect_free(compressor);
4262 if (value) {
4263 AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", template(sym));
4264 return make_node(AST_SimpleStatement, node, {
4265 body: value
4266 });
4267 }
4268 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
4269 } else if (node instanceof AST_Sequence) {
4270 if (node.expressions.length == 1) return node.expressions[0];
4271 }
4272 });
4273 tt.push(compressor.parent());
4274 self.transform(tt);
4275 unused_fn_names.forEach(function(fn) {
4276 fn.name = null;
4277 });
4278 calls_to_drop_args.forEach(function(call) {
4279 drop_unused_call_args(call, compressor, fns_with_marked_args);
4280 });
4281
4282 function log(sym, text, props) {
4283 AST_Node[sym.unreferenced() ? "warn" : "info"](text, props);
4284 }
4285
4286 function template(sym) {
4287 return {
4288 name: sym.name,
4289 file: sym.start.file,
4290 line: sym.start.line,
4291 col : sym.start.col
4292 };
4293 }
4294
4295 function verify_safe_usage(def, read, modified) {
4296 if (def.id in in_use_ids) return;
4297 if (read && modified) {
4298 in_use_ids[def.id] = true;
4299 in_use.push(def);
4300 } else {
4301 value_read[def.id] = read;
4302 value_modified[def.id] = modified;
4303 }
4304 }
4305
4306 function get_rhs(assign) {
4307 var rhs = assign.right;
4308 if (!assign.write_only) return rhs;
4309 if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
4310 var sym = assign.left;
4311 if (!(sym instanceof AST_SymbolRef) || sym.name != rhs.left.name) return rhs;
4312 return rhs.right.has_side_effects(compressor) ? rhs : rhs.right;
4313 }
4314
4315 function scan_ref_scoped(node, descend, init) {
4316 var node_def, props = [], sym = assign_as_unused(node, props);
4317 if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
4318 props.forEach(function(prop) {
4319 prop.walk(tw);
4320 });
4321 if (node instanceof AST_Assign) {
4322 if (node.write_only === "p" && node.right.may_throw_on_access(compressor)) return;
4323 var right = get_rhs(node);
4324 if (init && node.write_only === true && node_def.scope === self && !right.has_side_effects(compressor)) {
4325 initializations.add(node_def.id, right);
4326 } else {
4327 right.walk(tw);
4328 }
4329 if (node.left === sym) {
4330 if (!node_def.chained && sym.fixed_value(true) === right) {
4331 fixed_ids[node_def.id] = node;
4332 }
4333 if (!node.write_only) {
4334 verify_safe_usage(node_def, true, value_modified[node_def.id]);
4335 }
4336 } else {
4337 var fixed = sym.fixed_value();
4338 if (!fixed || !fixed.is_constant()) {
4339 verify_safe_usage(node_def, value_read[node_def.id], true);
4340 }
4341 }
4342 }
4343 return true;
4344 }
4345 if (node instanceof AST_SymbolRef) {
4346 node_def = node.definition();
4347 if (!(node_def.id in in_use_ids)) {
4348 in_use_ids[node_def.id] = true;
4349 in_use.push(node_def);
4350 }
4351 return true;
4352 }
4353 if (node instanceof AST_Scope) {
4354 var save_scope = scope;
4355 scope = node;
4356 descend();
4357 scope = save_scope;
4358 return true;
4359 }
4360 }
4361 });
4362
4363 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
4364 if (compressor.has_directive("use asm")) return;
4365 var hoist_funs = compressor.option("hoist_funs");
4366 var hoist_vars = compressor.option("hoist_vars");
4367 var self = this;
4368 if (hoist_vars) {
4369 // let's count var_decl first, we seem to waste a lot of
4370 // space if we hoist `var` when there's only one.
4371 var var_decl = 0;
4372 self.walk(new TreeWalker(function(node) {
4373 if (var_decl > 1) return true;
4374 if (node instanceof AST_Scope && node !== self) return true;
4375 if (node instanceof AST_Var) {
4376 var_decl++;
4377 return true;
4378 }
4379 }));
4380 if (var_decl <= 1) hoist_vars = false;
4381 }
4382 if (!hoist_funs && !hoist_vars) return;
4383 var dirs = [];
4384 var hoisted = [];
4385 var vars = new Dictionary(), vars_found = 0;
4386 var tt = new TreeTransformer(function(node) {
4387 if (node === self) return;
4388 if (node instanceof AST_Directive) {
4389 dirs.push(node);
4390 return make_node(AST_EmptyStatement, node);
4391 }
4392 if (hoist_funs && node instanceof AST_Defun
4393 && (tt.parent() === self || !compressor.has_directive("use strict"))) {
4394 hoisted.push(node);
4395 return make_node(AST_EmptyStatement, node);
4396 }
4397 if (hoist_vars && node instanceof AST_Var) {
4398 node.definitions.forEach(function(def) {
4399 vars.set(def.name.name, def);
4400 ++vars_found;
4401 });
4402 var seq = node.to_assignments(compressor);
4403 var p = tt.parent();
4404 if (p instanceof AST_ForIn && p.init === node) {
4405 if (seq) return seq;
4406 var def = node.definitions[0].name;
4407 return make_node(AST_SymbolRef, def, def);
4408 }
4409 if (p instanceof AST_For && p.init === node) return seq;
4410 if (!seq) return make_node(AST_EmptyStatement, node);
4411 return make_node(AST_SimpleStatement, node, {
4412 body: seq
4413 });
4414 }
4415 if (node instanceof AST_Scope) return node;
4416 });
4417 self.transform(tt);
4418 if (vars_found > 0) {
4419 // collect only vars which don't show up in self's arguments list
4420 var defs = [];
4421 vars.each(function(def, name) {
4422 if (self instanceof AST_Lambda
4423 && !all(self.argnames, function(argname) {
4424 return argname.name != name;
4425 })) {
4426 vars.del(name);
4427 } else {
4428 def = def.clone();
4429 def.value = null;
4430 defs.push(def);
4431 vars.set(name, def);
4432 }
4433 });
4434 if (defs.length > 0) {
4435 // try to merge in assignments
4436 for (var i = 0; i < self.body.length;) {
4437 if (self.body[i] instanceof AST_SimpleStatement) {
4438 var expr = self.body[i].body, sym, assign;
4439 if (expr instanceof AST_Assign
4440 && expr.operator == "="
4441 && (sym = expr.left) instanceof AST_Symbol
4442 && vars.has(sym.name))
4443 {
4444 var def = vars.get(sym.name);
4445 if (def.value) break;
4446 def.value = expr.right;
4447 remove(defs, def);
4448 defs.push(def);
4449 self.body.splice(i, 1);
4450 continue;
4451 }
4452 if (expr instanceof AST_Sequence
4453 && (assign = expr.expressions[0]) instanceof AST_Assign
4454 && assign.operator == "="
4455 && (sym = assign.left) instanceof AST_Symbol
4456 && vars.has(sym.name))
4457 {
4458 var def = vars.get(sym.name);
4459 if (def.value) break;
4460 def.value = assign.right;
4461 remove(defs, def);
4462 defs.push(def);
4463 self.body[i].body = make_sequence(expr, expr.expressions.slice(1));
4464 continue;
4465 }
4466 }
4467 if (self.body[i] instanceof AST_EmptyStatement) {
4468 self.body.splice(i, 1);
4469 continue;
4470 }
4471 if (self.body[i] instanceof AST_BlockStatement) {
4472 var tmp = [ i, 1 ].concat(self.body[i].body);
4473 self.body.splice.apply(self.body, tmp);
4474 continue;
4475 }
4476 break;
4477 }
4478 defs = make_node(AST_Var, self, {
4479 definitions: defs
4480 });
4481 hoisted.push(defs);
4482 }
4483 }
4484 self.body = dirs.concat(hoisted, self.body);
4485 });
4486
4487 function scan_local_returns(fn, transform) {
4488 fn.walk(new TreeWalker(function(node) {
4489 if (node instanceof AST_Return) {
4490 transform(node);
4491 return true;
4492 }
4493 if (node instanceof AST_Scope && node !== fn) return true;
4494 }));
4495 }
4496
4497 function map_bool_returns(fn) {
4498 var map = Object.create(null);
4499 scan_local_returns(fn, function(node) {
4500 var value = node.value;
4501 if (value) value = value.tail_node();
4502 if (value instanceof AST_SymbolRef) {
4503 var id = value.definition().id;
4504 map[id] = (map[id] || 0) + 1;
4505 }
4506 });
4507 return map;
4508 }
4509
4510 function all_bool(def, bool_returns, compressor) {
4511 return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length
4512 && !compressor.exposed(def);
4513 }
4514
4515 function process_boolean_returns(fn, compressor) {
4516 scan_local_returns(fn, function(node) {
4517 node.in_bool = true;
4518 var value = node.value;
4519 if (value) {
4520 var ev = value.is_truthy() || value.evaluate(compressor, true);
4521 if (!ev) {
4522 value = value.drop_side_effect_free(compressor);
4523 node.value = value ? make_sequence(node.value, [
4524 value,
4525 make_node(AST_Number, node.value, {
4526 value: 0
4527 })
4528 ]) : null;
4529 } else if (ev && !(ev instanceof AST_Node)) {
4530 value = value.drop_side_effect_free(compressor);
4531 node.value = value ? make_sequence(node.value, [
4532 value,
4533 make_node(AST_Number, node.value, {
4534 value: 1
4535 })
4536 ]) : make_node(AST_Number, node.value, {
4537 value: 1
4538 });
4539 }
4540 }
4541 });
4542 }
4543
4544 AST_Scope.DEFMETHOD("process_boolean_returns", noop);
4545 AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
4546 if (!compressor.option("booleans")) return;
4547 var bool_returns = map_bool_returns(this);
4548 if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
4549 process_boolean_returns(this, compressor);
4550 });
4551 AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
4552 if (!compressor.option("booleans")) return;
4553 var bool_returns = map_bool_returns(this);
4554 if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
4555 var parent = compressor.parent();
4556 if (parent instanceof AST_Assign) {
4557 if (parent.operator != "=") return;
4558 var sym = parent.left;
4559 if (!(sym instanceof AST_SymbolRef)) return;
4560 if (!all_bool(sym.definition(), bool_returns, compressor)) return;
4561 } else if (parent instanceof AST_Call && parent.expression !== this) {
4562 var exp = parent.expression;
4563 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
4564 if (!(exp instanceof AST_Lambda)) return;
4565 if (exp.uses_arguments || exp.pinned()) return;
4566 var sym = exp.argnames[parent.args.indexOf(this)];
4567 if (sym && !all_bool(sym.definition(), bool_returns, compressor)) return;
4568 } else if (parent.TYPE == "Call") {
4569 compressor.pop();
4570 var in_bool = compressor.in_boolean_context();
4571 compressor.push(this);
4572 if (!in_bool) return;
4573 } else return;
4574 process_boolean_returns(this, compressor);
4575 });
4576
4577 AST_Scope.DEFMETHOD("var_names", function() {
4578 var var_names = this._var_names;
4579 if (!var_names) {
4580 this._var_names = var_names = Object.create(null);
4581 this.enclosed.forEach(function(def) {
4582 var_names[def.name] = true;
4583 });
4584 this.variables.each(function(def, name) {
4585 var_names[name] = true;
4586 });
4587 }
4588 return var_names;
4589 });
4590
4591 AST_Scope.DEFMETHOD("make_var_name", function(prefix) {
4592 var var_names = this.var_names();
4593 prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
4594 var name = prefix;
4595 for (var i = 0; var_names[name]; i++) name = prefix + "$" + i;
4596 var_names[name] = true;
4597 return name;
4598 });
4599
4600 AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
4601 if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
4602 var self = this;
4603 var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
4604 var defs_by_id = Object.create(null);
4605 self.transform(new TreeTransformer(function(node, descend) {
4606 if (node instanceof AST_Assign) {
4607 if (node.operator != "=") return;
4608 if (!node.write_only) return;
4609 if (node.left.scope !== self) return;
4610 if (!can_hoist(node.left, node.right, 1)) return;
4611 descend(node, this);
4612 var defs = new Dictionary();
4613 var assignments = [];
4614 var decls = [];
4615 node.right.properties.forEach(function(prop) {
4616 var decl = make_sym(node.left, prop.key);
4617 decls.push(make_node(AST_VarDef, node, {
4618 name: decl,
4619 value: null
4620 }));
4621 var sym = make_node(AST_SymbolRef, node, {
4622 name: decl.name,
4623 scope: self,
4624 thedef: decl.definition()
4625 });
4626 sym.reference({});
4627 assignments.push(make_node(AST_Assign, node, {
4628 operator: "=",
4629 left: sym,
4630 right: prop.value
4631 }));
4632 });
4633 defs_by_id[node.left.definition().id] = defs;
4634 self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
4635 definitions: decls
4636 }));
4637 return make_sequence(node, assignments);
4638 }
4639 if (node instanceof AST_Scope) return node === self ? undefined : node;
4640 if (node instanceof AST_VarDef) {
4641 if (!can_hoist(node.name, node.value, 0)) return;
4642 descend(node, this);
4643 var defs = new Dictionary();
4644 var var_defs = [];
4645 node.value.properties.forEach(function(prop) {
4646 var_defs.push(make_node(AST_VarDef, node, {
4647 name: make_sym(node.name, prop.key),
4648 value: prop.value
4649 }));
4650 });
4651 defs_by_id[node.name.definition().id] = defs;
4652 return List.splice(var_defs);
4653 }
4654
4655 function make_sym(sym, key) {
4656 var new_var = make_node(AST_SymbolVar, sym, {
4657 name: self.make_var_name(sym.name + "_" + key),
4658 scope: self
4659 });
4660 var def = self.def_variable(new_var);
4661 defs.set(key, def);
4662 self.enclosed.push(def);
4663 return new_var;
4664 }
4665 }));
4666 self.transform(new TreeTransformer(function(node, descend) {
4667 if (node instanceof AST_PropAccess) {
4668 if (!(node.expression instanceof AST_SymbolRef)) return;
4669 var defs = defs_by_id[node.expression.definition().id];
4670 if (!defs) return;
4671 var def = defs.get(node.getProperty());
4672 var sym = make_node(AST_SymbolRef, node, {
4673 name: def.name,
4674 scope: node.expression.scope,
4675 thedef: def
4676 });
4677 sym.reference({});
4678 return sym;
4679 }
4680 if (node instanceof AST_Unary) {
4681 if (unary_side_effects[node.operator]) return;
4682 if (!(node.expression instanceof AST_SymbolRef)) return;
4683 if (!(node.expression.definition().id in defs_by_id)) return;
4684 var opt = node.clone();
4685 opt.expression = make_node(AST_Object, node, {
4686 properties: []
4687 });
4688 return opt;
4689 }
4690 }));
4691
4692 function can_hoist(sym, right, count) {
4693 var def = sym.definition();
4694 if (def.assignments != count) return;
4695 if (def.direct_access) return;
4696 if (def.escaped.depth == 1) return;
4697 if (def.references.length == count) return;
4698 if (def.single_use) return;
4699 if (top_retain(def)) return;
4700 if (sym.fixed_value() !== right) return;
4701 return right instanceof AST_Object;
4702 }
4703 });
4704
4705 function safe_to_drop(fn, compressor) {
4706 if (!fn.name || !compressor.option("ie8")) return true;
4707 var def = fn.name.definition();
4708 if (compressor.exposed(def)) return false;
4709 return all(def.references, function(sym) {
4710 return !(sym instanceof AST_SymbolRef);
4711 });
4712 }
4713
4714 // drop_side_effect_free()
4715 // remove side-effect-free parts which only affects return value
4716 (function(def) {
4717 // Drop side-effect-free elements from an array of expressions.
4718 // Returns an array of expressions with side-effects or null
4719 // if all elements were dropped. Note: original array may be
4720 // returned if nothing changed.
4721 function trim(nodes, compressor, first_in_statement) {
4722 var len = nodes.length;
4723 if (!len) return null;
4724 var ret = [], changed = false;
4725 for (var i = 0; i < len; i++) {
4726 var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
4727 changed |= node !== nodes[i];
4728 if (node) {
4729 ret.push(node);
4730 first_in_statement = false;
4731 }
4732 }
4733 return changed ? ret.length ? ret : null : nodes;
4734 }
4735
4736 def(AST_Node, return_this);
4737 def(AST_Accessor, return_null);
4738 def(AST_Array, function(compressor, first_in_statement) {
4739 var values = trim(this.elements, compressor, first_in_statement);
4740 return values && make_sequence(this, values);
4741 });
4742 def(AST_Assign, function(compressor) {
4743 var left = this.left;
4744 if (left instanceof AST_PropAccess) {
4745 var expr = left.expression;
4746 if (expr instanceof AST_Assign && expr.operator == "=" && !expr.may_throw_on_access(compressor)) {
4747 expr.write_only = "p";
4748 }
4749 if (compressor.has_directive("use strict") && expr.is_constant()) return this;
4750 }
4751 if (left.has_side_effects(compressor)) return this;
4752 this.write_only = true;
4753 if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
4754 return this.right.drop_side_effect_free(compressor);
4755 }
4756 return this;
4757 });
4758 def(AST_Binary, function(compressor, first_in_statement) {
4759 var right = this.right.drop_side_effect_free(compressor, first_in_statement);
4760 if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
4761 if (lazy_op[this.operator] && !(right instanceof AST_Function)) {
4762 var node = this;
4763 if (right !== node.right) {
4764 node = this.clone();
4765 node.right = right.drop_side_effect_free(compressor);
4766 }
4767 return (first_in_statement ? best_of_statement : best_of_expression)(node, make_node(AST_Binary, this, {
4768 operator: node.operator == "&&" ? "||" : "&&",
4769 left: node.left.negate(compressor, first_in_statement),
4770 right: node.right
4771 }));
4772 } else {
4773 var left = this.left.drop_side_effect_free(compressor, first_in_statement);
4774 if (!left) return right;
4775 return make_sequence(this, [ left, right.drop_side_effect_free(compressor) ]);
4776 }
4777 });
4778 def(AST_Call, function(compressor, first_in_statement) {
4779 if (!this.is_expr_pure(compressor)) {
4780 var exp = this.expression;
4781 if (this.is_call_pure(compressor)) {
4782 var exprs = this.args.slice();
4783 exprs.unshift(exp.expression);
4784 exprs = trim(exprs, compressor, first_in_statement);
4785 return exprs && make_sequence(this, exprs);
4786 }
4787 if (exp instanceof AST_Function && (!exp.name || !exp.name.definition().references.length)) {
4788 exp.process_expression(false, function(node) {
4789 var value = node.value && node.value.drop_side_effect_free(compressor, true);
4790 return value ? make_node(AST_SimpleStatement, node, {
4791 body: value
4792 }) : make_node(AST_EmptyStatement, node);
4793 });
4794 scan_local_returns(exp, function(node) {
4795 if (node.value) node.value = node.value.drop_side_effect_free(compressor);
4796 });
4797 // always shallow clone to ensure stripping of negated IIFEs
4798 return this.clone();
4799 }
4800 return this;
4801 }
4802 if (this.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
4803 var args = trim(this.args, compressor, first_in_statement);
4804 return args && make_sequence(this, args);
4805 });
4806 def(AST_Conditional, function(compressor) {
4807 var consequent = this.consequent.drop_side_effect_free(compressor);
4808 var alternative = this.alternative.drop_side_effect_free(compressor);
4809 if (consequent === this.consequent && alternative === this.alternative) return this;
4810 if (!consequent) return alternative ? make_node(AST_Binary, this, {
4811 operator: "||",
4812 left: this.condition,
4813 right: alternative
4814 }) : this.condition.drop_side_effect_free(compressor);
4815 if (!alternative) return make_node(AST_Binary, this, {
4816 operator: "&&",
4817 left: this.condition,
4818 right: consequent
4819 });
4820 var node = this.clone();
4821 node.consequent = consequent;
4822 node.alternative = alternative;
4823 return node;
4824 });
4825 def(AST_Constant, return_null);
4826 def(AST_Dot, function(compressor, first_in_statement) {
4827 var expr = this.expression;
4828 if (expr.may_throw_on_access(compressor)) return this;
4829 return expr.drop_side_effect_free(compressor, first_in_statement);
4830 });
4831 def(AST_Function, function(compressor) {
4832 return safe_to_drop(this, compressor) ? null : this;
4833 });
4834 def(AST_Object, function(compressor, first_in_statement) {
4835 var values = trim(this.properties, compressor, first_in_statement);
4836 return values && make_sequence(this, values);
4837 });
4838 def(AST_ObjectProperty, function(compressor, first_in_statement) {
4839 return this.value.drop_side_effect_free(compressor, first_in_statement);
4840 });
4841 def(AST_Sequence, function(compressor, first_in_statement) {
4842 var expressions = trim(this.expressions, compressor, first_in_statement);
4843 if (expressions === this.expressions) return this;
4844 if (!expressions) return null;
4845 return make_sequence(this, expressions);
4846 });
4847 def(AST_Sub, function(compressor, first_in_statement) {
4848 if (this.expression.may_throw_on_access(compressor)) return this;
4849 var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
4850 if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
4851 var property = this.property.drop_side_effect_free(compressor);
4852 if (!property) return expression;
4853 return make_sequence(this, [ expression, property ]);
4854 });
4855 def(AST_SymbolRef, function(compressor) {
4856 if (!this.is_declared(compressor)) return this;
4857 this.definition().replaced++;
4858 return null;
4859 });
4860 def(AST_This, return_null);
4861 def(AST_Unary, function(compressor, first_in_statement) {
4862 if (unary_side_effects[this.operator]) {
4863 this.write_only = !this.expression.has_side_effects(compressor);
4864 return this;
4865 }
4866 if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) {
4867 this.expression.definition().replaced++;
4868 return null;
4869 }
4870 var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
4871 if (first_in_statement && expression && is_iife_call(expression)) {
4872 if (expression === this.expression && this.operator == "!") return this;
4873 return expression.negate(compressor, first_in_statement);
4874 }
4875 return expression;
4876 });
4877 })(function(node, func) {
4878 node.DEFMETHOD("drop_side_effect_free", func);
4879 });
4880
4881 OPT(AST_SimpleStatement, function(self, compressor) {
4882 if (compressor.option("side_effects")) {
4883 var body = self.body;
4884 var node = body.drop_side_effect_free(compressor, true);
4885 if (!node) {
4886 AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
4887 return make_node(AST_EmptyStatement, self);
4888 }
4889 if (node !== body) {
4890 return make_node(AST_SimpleStatement, self, { body: node });
4891 }
4892 }
4893 return self;
4894 });
4895
4896 OPT(AST_While, function(self, compressor) {
4897 return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
4898 });
4899
4900 function has_break_or_continue(loop, parent) {
4901 var found = false;
4902 var tw = new TreeWalker(function(node) {
4903 if (found || node instanceof AST_Scope) return true;
4904 if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) {
4905 return found = true;
4906 }
4907 });
4908 if (parent instanceof AST_LabeledStatement) tw.push(parent);
4909 tw.push(loop);
4910 loop.body.walk(tw);
4911 return found;
4912 }
4913
4914 OPT(AST_Do, function(self, compressor) {
4915 if (!compressor.option("loops")) return self;
4916 var cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
4917 if (!(cond instanceof AST_Node)) {
4918 if (cond) return make_node(AST_For, self, {
4919 body: make_node(AST_BlockStatement, self.body, {
4920 body: [
4921 self.body,
4922 make_node(AST_SimpleStatement, self.condition, {
4923 body: self.condition
4924 })
4925 ]
4926 })
4927 }).optimize(compressor);
4928 if (!has_break_or_continue(self, compressor.parent())) {
4929 return make_node(AST_BlockStatement, self.body, {
4930 body: [
4931 self.body,
4932 make_node(AST_SimpleStatement, self.condition, {
4933 body: self.condition
4934 })
4935 ]
4936 }).optimize(compressor);
4937 }
4938 }
4939 if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
4940 condition: make_sequence(self.condition, [
4941 self.body.body,
4942 self.condition
4943 ]),
4944 body: make_node(AST_EmptyStatement, self)
4945 }).optimize(compressor);
4946 return self;
4947 });
4948
4949 function if_break_in_loop(self, compressor) {
4950 var first = first_statement(self.body);
4951 if (compressor.option("dead_code")
4952 && (first instanceof AST_Break
4953 || first instanceof AST_Continue && external_target(first)
4954 || first instanceof AST_Exit)) {
4955 var body = [];
4956 if (self.init instanceof AST_Statement) {
4957 body.push(self.init);
4958 } else if (self.init) {
4959 body.push(make_node(AST_SimpleStatement, self.init, {
4960 body: self.init
4961 }));
4962 }
4963 var retain = external_target(first) || first instanceof AST_Exit;
4964 if (self.condition && retain) {
4965 body.push(make_node(AST_If, self, {
4966 condition: self.condition,
4967 body: first,
4968 alternative: null
4969 }));
4970 } else if (self.condition) {
4971 body.push(make_node(AST_SimpleStatement, self.condition, {
4972 body: self.condition
4973 }));
4974 } else if (retain) {
4975 body.push(first);
4976 }
4977 extract_declarations_from_unreachable_code(self.body, body);
4978 return make_node(AST_BlockStatement, self, {
4979 body: body
4980 });
4981 }
4982 if (first instanceof AST_If) {
4983 var ab = first_statement(first.body);
4984 if (ab instanceof AST_Break && !external_target(ab)) {
4985 if (self.condition) {
4986 self.condition = make_node(AST_Binary, self.condition, {
4987 left: self.condition,
4988 operator: "&&",
4989 right: first.condition.negate(compressor),
4990 });
4991 } else {
4992 self.condition = first.condition.negate(compressor);
4993 }
4994 var body = as_statement_array(first.alternative);
4995 extract_declarations_from_unreachable_code(first.body, body);
4996 return drop_it(body);
4997 }
4998 ab = first_statement(first.alternative);
4999 if (ab instanceof AST_Break && !external_target(ab)) {
5000 if (self.condition) {
5001 self.condition = make_node(AST_Binary, self.condition, {
5002 left: self.condition,
5003 operator: "&&",
5004 right: first.condition,
5005 });
5006 } else {
5007 self.condition = first.condition;
5008 }
5009 var body = as_statement_array(first.body);
5010 extract_declarations_from_unreachable_code(first.alternative, body);
5011 return drop_it(body);
5012 }
5013 }
5014 return self;
5015
5016 function first_statement(body) {
5017 return body instanceof AST_BlockStatement ? body.body[0] : body;
5018 }
5019
5020 function external_target(node) {
5021 return compressor.loopcontrol_target(node) !== compressor.self();
5022 }
5023
5024 function drop_it(rest) {
5025 if (self.body instanceof AST_BlockStatement) {
5026 self.body = self.body.clone();
5027 self.body.body = rest.concat(self.body.body.slice(1));
5028 self.body = self.body.transform(compressor);
5029 } else {
5030 self.body = make_node(AST_BlockStatement, self.body, {
5031 body: rest
5032 }).transform(compressor);
5033 }
5034 return if_break_in_loop(self, compressor);
5035 }
5036 }
5037
5038 OPT(AST_For, function(self, compressor) {
5039 if (!compressor.option("loops")) return self;
5040 if (compressor.option("side_effects")) {
5041 if (self.init) self.init = self.init.drop_side_effect_free(compressor);
5042 if (self.step) self.step = self.step.drop_side_effect_free(compressor);
5043 }
5044 if (self.condition) {
5045 var cond = self.condition.evaluate(compressor);
5046 if (cond instanceof AST_Node) {
5047 cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
5048 } else if (cond) {
5049 self.condition = null;
5050 } else if (!compressor.option("dead_code")) {
5051 var orig = self.condition;
5052 self.condition = make_node_from_constant(cond, self.condition);
5053 self.condition = best_of_expression(self.condition.transform(compressor), orig);
5054 }
5055 if (!cond) {
5056 if (compressor.option("dead_code")) {
5057 var body = [];
5058 extract_declarations_from_unreachable_code(self.body, body);
5059 if (self.init instanceof AST_Statement) {
5060 body.push(self.init);
5061 } else if (self.init) {
5062 body.push(make_node(AST_SimpleStatement, self.init, {
5063 body: self.init
5064 }));
5065 }
5066 body.push(make_node(AST_SimpleStatement, self.condition, {
5067 body: self.condition
5068 }));
5069 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
5070 }
5071 } else if (self.condition && !(cond instanceof AST_Node)) {
5072 self.body = make_node(AST_BlockStatement, self.body, {
5073 body: [
5074 make_node(AST_SimpleStatement, self.condition, {
5075 body: self.condition
5076 }),
5077 self.body
5078 ]
5079 });
5080 self.condition = null;
5081 }
5082 }
5083 return if_break_in_loop(self, compressor);
5084 });
5085
5086 function mark_locally_defined(condition, consequent, alternative, operator) {
5087 if (!(condition instanceof AST_Binary)) return;
5088 if (!(condition.left instanceof AST_String)) {
5089 if (!operator) operator = condition.operator;
5090 if (condition.operator != operator) return;
5091 switch (operator) {
5092 case "&&":
5093 case "||":
5094 mark_locally_defined(condition.left, consequent, alternative, operator);
5095 mark_locally_defined(condition.right, consequent, alternative, operator);
5096 break;
5097 }
5098 return;
5099 }
5100 if (!(condition.right instanceof AST_UnaryPrefix)) return;
5101 if (condition.right.operator != "typeof") return;
5102 var sym = condition.right.expression;
5103 if (!is_undeclared_ref(sym)) return;
5104 var body;
5105 var undef = condition.left.value == "undefined";
5106 switch (condition.operator) {
5107 case "==":
5108 body = undef ? alternative : consequent;
5109 break;
5110 case "!=":
5111 body = undef ? consequent : alternative;
5112 break;
5113 default:
5114 return;
5115 }
5116 if (!body) return;
5117 var def = sym.definition();
5118 var tw = new TreeWalker(function(node) {
5119 if (node instanceof AST_Scope) {
5120 var parent = tw.parent();
5121 if (parent instanceof AST_Call && parent.expression === node) return;
5122 return true;
5123 }
5124 if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
5125 });
5126 body.walk(tw);
5127 }
5128
5129 OPT(AST_If, function(self, compressor) {
5130 if (is_empty(self.alternative)) self.alternative = null;
5131
5132 if (!compressor.option("conditionals")) return self;
5133 // if condition can be statically determined, warn and drop
5134 // one of the blocks. note, statically determined implies
5135 // “has no side effects”; also it doesn't work for cases like
5136 // `x && true`, though it probably should.
5137 var cond = self.condition.evaluate(compressor);
5138 if (!compressor.option("dead_code") && !(cond instanceof AST_Node)) {
5139 var orig = self.condition;
5140 self.condition = make_node_from_constant(cond, orig);
5141 self.condition = best_of_expression(self.condition.transform(compressor), orig);
5142 }
5143 if (compressor.option("dead_code")) {
5144 if (cond instanceof AST_Node) {
5145 cond = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
5146 }
5147 if (!cond) {
5148 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
5149 var body = [];
5150 extract_declarations_from_unreachable_code(self.body, body);
5151 body.push(make_node(AST_SimpleStatement, self.condition, {
5152 body: self.condition
5153 }));
5154 if (self.alternative) body.push(self.alternative);
5155 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
5156 } else if (!(cond instanceof AST_Node)) {
5157 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
5158 var body = [];
5159 if (self.alternative) extract_declarations_from_unreachable_code(self.alternative, body);
5160 body.push(make_node(AST_SimpleStatement, self.condition, {
5161 body: self.condition
5162 }));
5163 body.push(self.body);
5164 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
5165 }
5166 }
5167 var negated = self.condition.negate(compressor);
5168 var self_condition_length = self.condition.print_to_string().length;
5169 var negated_length = negated.print_to_string().length;
5170 var negated_is_best = negated_length < self_condition_length;
5171 if (self.alternative && negated_is_best) {
5172 negated_is_best = false; // because we already do the switch here.
5173 // no need to swap values of self_condition_length and negated_length
5174 // here because they are only used in an equality comparison later on.
5175 self.condition = negated;
5176 var tmp = self.body;
5177 self.body = self.alternative || make_node(AST_EmptyStatement, self);
5178 self.alternative = tmp;
5179 }
5180 if (self.body instanceof AST_SimpleStatement
5181 && self.alternative instanceof AST_SimpleStatement) {
5182 return make_node(AST_SimpleStatement, self, {
5183 body: make_node(AST_Conditional, self, {
5184 condition : self.condition,
5185 consequent : self.body.body,
5186 alternative : self.alternative.body
5187 })
5188 }).optimize(compressor);
5189 }
5190 if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
5191 if (self_condition_length === negated_length && !negated_is_best
5192 && self.condition instanceof AST_Binary && self.condition.operator == "||") {
5193 // although the code length of self.condition and negated are the same,
5194 // negated does not require additional surrounding parentheses.
5195 // see https://github.com/mishoo/UglifyJS2/issues/979
5196 negated_is_best = true;
5197 }
5198 if (negated_is_best) return make_node(AST_SimpleStatement, self, {
5199 body: make_node(AST_Binary, self, {
5200 operator : "||",
5201 left : negated,
5202 right : self.body.body
5203 }).transform(compressor)
5204 }).optimize(compressor);
5205 return make_node(AST_SimpleStatement, self, {
5206 body: make_node(AST_Binary, self, {
5207 operator : "&&",
5208 left : self.condition,
5209 right : self.body.body
5210 }).transform(compressor)
5211 }).optimize(compressor);
5212 }
5213 if (is_empty(self.body)) {
5214 if (is_empty(self.alternative)) return make_node(AST_SimpleStatement, self.condition, {
5215 body: self.condition.clone()
5216 }).optimize(compressor);
5217 if (self.alternative instanceof AST_SimpleStatement) return make_node(AST_SimpleStatement, self, {
5218 body: make_node(AST_Binary, self, {
5219 operator : "||",
5220 left : self.condition,
5221 right : self.alternative.body
5222 }).transform(compressor)
5223 }).optimize(compressor);
5224 self = make_node(AST_If, self, {
5225 condition: negated,
5226 body: self.alternative,
5227 alternative: null
5228 });
5229 }
5230 if (self.body instanceof AST_Exit
5231 && self.alternative instanceof AST_Exit
5232 && self.body.TYPE == self.alternative.TYPE) {
5233 var exit = make_node(self.body.CTOR, self, {
5234 value: make_node(AST_Conditional, self, {
5235 condition : self.condition,
5236 consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
5237 alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
5238 })
5239 });
5240 if (exit instanceof AST_Return) {
5241 exit.in_bool = self.body.in_bool || self.alternative.in_bool;
5242 }
5243 return exit;
5244 }
5245 if (self.body instanceof AST_If
5246 && !self.body.alternative
5247 && !self.alternative) {
5248 self = make_node(AST_If, self, {
5249 condition: make_node(AST_Binary, self.condition, {
5250 operator: "&&",
5251 left: self.condition,
5252 right: self.body.condition
5253 }),
5254 body: self.body.body,
5255 alternative: null
5256 });
5257 }
5258 if (aborts(self.body)) {
5259 if (self.alternative) {
5260 var alt = self.alternative;
5261 self.alternative = null;
5262 return make_node(AST_BlockStatement, self, {
5263 body: [ self, alt ]
5264 }).optimize(compressor);
5265 }
5266 }
5267 if (aborts(self.alternative)) {
5268 var body = self.body;
5269 self.body = self.alternative;
5270 self.condition = negated_is_best ? negated : self.condition.negate(compressor);
5271 self.alternative = null;
5272 return make_node(AST_BlockStatement, self, {
5273 body: [ self, body ]
5274 }).optimize(compressor);
5275 }
5276 if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
5277 return self;
5278 });
5279
5280 OPT(AST_Switch, function(self, compressor) {
5281 if (!compressor.option("switches")) return self;
5282 var branch;
5283 var value = self.expression.evaluate(compressor);
5284 if (!(value instanceof AST_Node)) {
5285 var orig = self.expression;
5286 self.expression = make_node_from_constant(value, orig);
5287 self.expression = best_of_expression(self.expression.transform(compressor), orig);
5288 }
5289 if (!compressor.option("dead_code")) return self;
5290 if (value instanceof AST_Node) {
5291 value = self.expression.evaluate(compressor, true);
5292 }
5293 var decl = [];
5294 var body = [];
5295 var default_branch;
5296 var exact_match;
5297 for (var i = 0, len = self.body.length; i < len && !exact_match; i++) {
5298 branch = self.body[i];
5299 if (branch instanceof AST_Default) {
5300 var prev = body[body.length - 1];
5301 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
5302 eliminate_branch(branch, prev);
5303 continue;
5304 } else {
5305 default_branch = branch;
5306 }
5307 } else if (!(value instanceof AST_Node)) {
5308 var exp = branch.expression.evaluate(compressor);
5309 if (!(exp instanceof AST_Node) && exp !== value) {
5310 eliminate_branch(branch, body[body.length - 1]);
5311 continue;
5312 }
5313 if (exp instanceof AST_Node) exp = branch.expression.evaluate(compressor, true);
5314 if (exp === value) {
5315 exact_match = branch;
5316 if (default_branch) {
5317 var default_index = body.indexOf(default_branch);
5318 body.splice(default_index, 1);
5319 eliminate_branch(default_branch, body[default_index - 1]);
5320 default_branch = null;
5321 }
5322 }
5323 }
5324 if (aborts(branch)) {
5325 var prev = body[body.length - 1];
5326 if (aborts(prev) && prev.body.length == branch.body.length
5327 && make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) {
5328 prev.body = [];
5329 }
5330 }
5331 body.push(branch);
5332 }
5333 while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
5334 while (branch = body[body.length - 1]) {
5335 var stat = branch.body[branch.body.length - 1];
5336 if (is_break(stat, compressor)) branch.body.pop();
5337 if (branch === default_branch) {
5338 if (!is_body_empty(branch)) break;
5339 } else if (branch.expression.has_side_effects(compressor)) {
5340 break;
5341 } else if (default_branch) {
5342 if (!is_body_empty(default_branch)) break;
5343 if (body[body.length - 2] !== default_branch) break;
5344 default_branch.body = default_branch.body.concat(branch.body);
5345 branch.body = [];
5346 } else if (!is_body_empty(branch)) break;
5347 eliminate_branch(branch);
5348 if (body.pop() === default_branch) default_branch = null;
5349 }
5350 if (body.length == 0) {
5351 return make_node(AST_BlockStatement, self, {
5352 body: decl.concat(make_node(AST_SimpleStatement, self.expression, {
5353 body: self.expression
5354 }))
5355 }).optimize(compressor);
5356 }
5357 body[0].body = decl.concat(body[0].body);
5358 self.body = body;
5359 if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) {
5360 var has_break = false;
5361 var tw = new TreeWalker(function(node) {
5362 if (has_break
5363 || node instanceof AST_Lambda
5364 || node instanceof AST_SimpleStatement) return true;
5365 if (is_break(node, tw)) has_break = true;
5366 });
5367 self.walk(tw);
5368 if (!has_break) {
5369 var statements = body[0].body.slice();
5370 var exp = body[0].expression;
5371 if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
5372 body: exp
5373 }));
5374 statements.unshift(make_node(AST_SimpleStatement, self.expression, {
5375 body:self.expression
5376 }));
5377 return make_node(AST_BlockStatement, self, {
5378 body: statements
5379 }).optimize(compressor);
5380 }
5381 }
5382 return self;
5383
5384 function is_break(node, tw) {
5385 return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
5386 }
5387
5388 function is_body_empty(branch) {
5389 return all(branch.body, function(stat) {
5390 return is_empty(stat)
5391 || stat instanceof AST_Defun
5392 || stat instanceof AST_Var && all(stat.definitions, function(var_def) {
5393 return !var_def.value;
5394 });
5395 });
5396 }
5397
5398 function eliminate_branch(branch, prev) {
5399 if (prev && !aborts(prev)) {
5400 prev.body = prev.body.concat(branch.body);
5401 } else {
5402 extract_declarations_from_unreachable_code(branch, decl);
5403 }
5404 }
5405 });
5406
5407 OPT(AST_Try, function(self, compressor) {
5408 self.body = tighten_body(self.body, compressor);
5409 if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
5410 if (compressor.option("dead_code") && all(self.body, is_empty)) {
5411 var body = [];
5412 if (self.bcatch) {
5413 extract_declarations_from_unreachable_code(self.bcatch, body);
5414 body.forEach(function(stat) {
5415 if (!(stat instanceof AST_Definitions)) return;
5416 stat.definitions.forEach(function(var_def) {
5417 var def = var_def.name.definition().redefined();
5418 if (!def) return;
5419 var_def.name = var_def.name.clone();
5420 var_def.name.thedef = def;
5421 });
5422 });
5423 }
5424 if (self.bfinally) body = body.concat(self.bfinally.body);
5425 return make_node(AST_BlockStatement, self, {
5426 body: body
5427 }).optimize(compressor);
5428 }
5429 return self;
5430 });
5431
5432 AST_Definitions.DEFMETHOD("remove_initializers", function() {
5433 this.definitions.forEach(function(def) {
5434 def.value = null;
5435 });
5436 });
5437
5438 AST_Definitions.DEFMETHOD("to_assignments", function(compressor) {
5439 var reduce_vars = compressor.option("reduce_vars");
5440 var assignments = this.definitions.reduce(function(a, def) {
5441 if (def.value) {
5442 var name = make_node(AST_SymbolRef, def.name, def.name);
5443 a.push(make_node(AST_Assign, def, {
5444 operator : "=",
5445 left : name,
5446 right : def.value
5447 }));
5448 if (reduce_vars) name.definition().fixed = false;
5449 }
5450 def = def.name.definition();
5451 def.eliminated++;
5452 def.replaced--;
5453 return a;
5454 }, []);
5455 if (assignments.length == 0) return null;
5456 return make_sequence(this, assignments);
5457 });
5458
5459 OPT(AST_Definitions, function(self, compressor) {
5460 return self.definitions.length ? self : make_node(AST_EmptyStatement, self);
5461 });
5462
5463 function lift_sequence_in_expression(node, compressor) {
5464 var exp = node.expression;
5465 if (!(exp instanceof AST_Sequence)) return node;
5466 var x = exp.expressions.slice();
5467 var e = node.clone();
5468 e.expression = x.pop();
5469 x.push(e);
5470 return make_sequence(node, x);
5471 }
5472
5473 function drop_unused_call_args(call, compressor, fns_with_marked_args) {
5474 var exp = call.expression;
5475 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
5476 if (!(fn instanceof AST_Lambda)) return;
5477 if (fn.uses_arguments) return;
5478 if (fn.pinned()) return;
5479 if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
5480 var args = call.args;
5481 var pos = 0, last = 0;
5482 var drop_fargs = fn === exp && !fn.name && compressor.drop_fargs(fn, call);
5483 var side_effects = [];
5484 for (var i = 0; i < args.length; i++) {
5485 var trim = i >= fn.argnames.length;
5486 if (trim || fn.argnames[i].__unused) {
5487 var node = args[i].drop_side_effect_free(compressor);
5488 if (drop_fargs) {
5489 fn.argnames.splice(i, 1);
5490 args.splice(i, 1);
5491 if (node) side_effects.push(node);
5492 i--;
5493 continue;
5494 } else if (node) {
5495 side_effects.push(node);
5496 args[pos++] = make_sequence(call, side_effects);
5497 side_effects = [];
5498 } else if (!trim) {
5499 if (side_effects.length) {
5500 node = make_sequence(call, side_effects);
5501 side_effects = [];
5502 } else {
5503 node = make_node(AST_Number, args[i], {
5504 value: 0
5505 });
5506 }
5507 args[pos++] = node;
5508 continue;
5509 }
5510 } else {
5511 side_effects.push(args[i]);
5512 args[pos++] = make_sequence(call, side_effects);
5513 side_effects = [];
5514 }
5515 last = pos;
5516 }
5517 if (drop_fargs) for (; i < fn.argnames.length; i++) {
5518 if (fn.argnames[i].__unused) fn.argnames.splice(i--, 1);
5519 }
5520 args.length = last;
5521 if (!side_effects.length) return;
5522 var arg = make_sequence(call, side_effects);
5523 args.push(args.length < fn.argnames.length ? make_node(AST_UnaryPrefix, call, {
5524 operator: "void",
5525 expression: arg
5526 }) : arg);
5527 }
5528
5529 OPT(AST_Call, function(self, compressor) {
5530 var exp = self.expression;
5531 if (compressor.option("sequences")) {
5532 if (exp instanceof AST_PropAccess) {
5533 var seq = lift_sequence_in_expression(exp, compressor);
5534 if (seq !== exp) {
5535 var call = self.clone();
5536 call.expression = seq.expressions.pop();
5537 seq.expressions.push(call);
5538 return seq.optimize(compressor);
5539 }
5540 } else if (!needs_unbinding(compressor, exp.tail_node())) {
5541 var seq = lift_sequence_in_expression(self, compressor);
5542 if (seq !== self) return seq.optimize(compressor);
5543 }
5544 }
5545 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
5546 if (compressor.option("unsafe")) {
5547 if (is_undeclared_ref(exp)) switch (exp.name) {
5548 case "Array":
5549 if (self.args.length == 1) {
5550 var first = self.args[0];
5551 if (first instanceof AST_Number) try {
5552 var length = first.value;
5553 if (length > 6) break;
5554 var elements = Array(length);
5555 for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
5556 return make_node(AST_Array, self, {
5557 elements: elements
5558 });
5559 } catch (ex) {
5560 AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
5561 length: length,
5562 file: self.start.file,
5563 line: self.start.line,
5564 col: self.start.col
5565 });
5566 break;
5567 }
5568 if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
5569 }
5570 return make_node(AST_Array, self, {
5571 elements: self.args
5572 });
5573 case "Object":
5574 if (self.args.length == 0) {
5575 return make_node(AST_Object, self, {
5576 properties: []
5577 });
5578 }
5579 break;
5580 case "String":
5581 if (self.args.length == 0) return make_node(AST_String, self, {
5582 value: ""
5583 });
5584 if (self.args.length <= 1) return make_node(AST_Binary, self, {
5585 left: self.args[0],
5586 operator: "+",
5587 right: make_node(AST_String, self, { value: "" })
5588 }).optimize(compressor);
5589 break;
5590 case "Number":
5591 if (self.args.length == 0) return make_node(AST_Number, self, {
5592 value: 0
5593 });
5594 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
5595 expression: self.args[0],
5596 operator: "+"
5597 }).optimize(compressor);
5598 case "Boolean":
5599 if (self.args.length == 0) return make_node(AST_False, self);
5600 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
5601 expression: make_node(AST_UnaryPrefix, self, {
5602 expression: self.args[0],
5603 operator: "!"
5604 }),
5605 operator: "!"
5606 }).optimize(compressor);
5607 break;
5608 case "RegExp":
5609 var params = [];
5610 if (all(self.args, function(arg) {
5611 var value = arg.evaluate(compressor);
5612 params.unshift(value);
5613 return arg !== value;
5614 })) {
5615 try {
5616 return best_of(compressor, self, make_node(AST_RegExp, self, {
5617 value: RegExp.apply(RegExp, params),
5618 }));
5619 } catch (ex) {
5620 AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
5621 expr: self.print_to_string(),
5622 file: self.start.file,
5623 line: self.start.line,
5624 col: self.start.col
5625 });
5626 }
5627 }
5628 break;
5629 } else if (exp instanceof AST_Dot) switch(exp.property) {
5630 case "toString":
5631 if (self.args.length == 0 && !exp.expression.may_throw_on_access(compressor)) {
5632 return make_node(AST_Binary, self, {
5633 left: make_node(AST_String, self, { value: "" }),
5634 operator: "+",
5635 right: exp.expression
5636 }).optimize(compressor);
5637 }
5638 break;
5639 case "join":
5640 if (exp.expression instanceof AST_Array) EXIT: {
5641 var separator;
5642 if (self.args.length > 0) {
5643 separator = self.args[0].evaluate(compressor);
5644 if (separator === self.args[0]) break EXIT; // not a constant
5645 }
5646 var elements = [];
5647 var consts = [];
5648 exp.expression.elements.forEach(function(el) {
5649 var value = el.evaluate(compressor);
5650 if (value !== el) {
5651 consts.push(value);
5652 } else {
5653 if (consts.length > 0) {
5654 elements.push(make_node(AST_String, self, {
5655 value: consts.join(separator)
5656 }));
5657 consts.length = 0;
5658 }
5659 elements.push(el);
5660 }
5661 });
5662 if (consts.length > 0) {
5663 elements.push(make_node(AST_String, self, {
5664 value: consts.join(separator)
5665 }));
5666 }
5667 if (elements.length == 0) return make_node(AST_String, self, { value: "" });
5668 if (elements.length == 1) {
5669 if (elements[0].is_string(compressor)) {
5670 return elements[0];
5671 }
5672 return make_node(AST_Binary, elements[0], {
5673 operator : "+",
5674 left : make_node(AST_String, self, { value: "" }),
5675 right : elements[0]
5676 });
5677 }
5678 if (separator == "") {
5679 var first;
5680 if (elements[0].is_string(compressor)
5681 || elements[1].is_string(compressor)) {
5682 first = elements.shift();
5683 } else {
5684 first = make_node(AST_String, self, { value: "" });
5685 }
5686 return elements.reduce(function(prev, el) {
5687 return make_node(AST_Binary, el, {
5688 operator : "+",
5689 left : prev,
5690 right : el
5691 });
5692 }, first).optimize(compressor);
5693 }
5694 // need this awkward cloning to not affect original element
5695 // best_of will decide which one to get through.
5696 var node = self.clone();
5697 node.expression = node.expression.clone();
5698 node.expression.expression = node.expression.expression.clone();
5699 node.expression.expression.elements = elements;
5700 return best_of(compressor, self, node);
5701 }
5702 break;
5703 case "charAt":
5704 if (self.args.length < 2) {
5705 var node = make_node(AST_Sub, self, {
5706 expression: exp.expression,
5707 property: self.args.length ? make_node(AST_Binary, self.args[0], {
5708 operator: "|",
5709 left: make_node(AST_Number, self, {
5710 value: 0
5711 }),
5712 right: self.args[0]
5713 }) : make_node(AST_Number, self, {
5714 value: 0
5715 })
5716 });
5717 node.is_string = return_true;
5718 return node.optimize(compressor);
5719 }
5720 break;
5721 case "apply":
5722 if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
5723 var args = self.args[1].elements.slice();
5724 args.unshift(self.args[0]);
5725 return make_node(AST_Call, self, {
5726 expression: make_node(AST_Dot, exp, {
5727 expression: exp.expression,
5728 property: "call"
5729 }),
5730 args: args
5731 }).optimize(compressor);
5732 }
5733 break;
5734 case "call":
5735 var func = exp.expression;
5736 if (func instanceof AST_SymbolRef) {
5737 func = func.fixed_value();
5738 }
5739 if (func instanceof AST_Lambda && !func.contains_this()) {
5740 return (self.args.length ? make_sequence(this, [
5741 self.args[0],
5742 make_node(AST_Call, self, {
5743 expression: exp.expression,
5744 args: self.args.slice(1)
5745 })
5746 ]) : make_node(AST_Call, self, {
5747 expression: exp.expression,
5748 args: []
5749 })).optimize(compressor);
5750 }
5751 break;
5752 }
5753 }
5754 if (compressor.option("unsafe_Function")
5755 && is_undeclared_ref(exp)
5756 && exp.name == "Function") {
5757 // new Function() => function(){}
5758 if (self.args.length == 0) return make_node(AST_Function, self, {
5759 argnames: [],
5760 body: []
5761 });
5762 if (all(self.args, function(x) {
5763 return x instanceof AST_String;
5764 })) {
5765 // quite a corner-case, but we can handle it:
5766 // https://github.com/mishoo/UglifyJS2/issues/203
5767 // if the code argument is a constant, then we can minify it.
5768 try {
5769 var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
5770 return arg.value;
5771 }).join(",") + "){" + self.args[self.args.length - 1].value + "})";
5772 var ast = parse(code);
5773 var mangle = { ie8: compressor.option("ie8") };
5774 ast.figure_out_scope(mangle);
5775 var comp = new Compressor(compressor.options);
5776 ast = ast.transform(comp);
5777 ast.figure_out_scope(mangle);
5778 ast.compute_char_frequency(mangle);
5779 ast.mangle_names(mangle);
5780 var fun;
5781 ast.walk(new TreeWalker(function(node) {
5782 if (fun) return true;
5783 if (node instanceof AST_Lambda) {
5784 fun = node;
5785 return true;
5786 }
5787 }));
5788 var code = OutputStream();
5789 AST_BlockStatement.prototype._codegen.call(fun, fun, code);
5790 self.args = [
5791 make_node(AST_String, self, {
5792 value: fun.argnames.map(function(arg) {
5793 return arg.print_to_string();
5794 }).join(",")
5795 }),
5796 make_node(AST_String, self.args[self.args.length - 1], {
5797 value: code.get().replace(/^\{|\}$/g, "")
5798 })
5799 ];
5800 return self;
5801 } catch (ex) {
5802 if (ex instanceof JS_Parse_Error) {
5803 AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
5804 AST_Node.warn(ex.toString());
5805 } else {
5806 throw ex;
5807 }
5808 }
5809 }
5810 }
5811 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
5812 var is_func = fn instanceof AST_Lambda;
5813 var stat = is_func && fn.first_statement();
5814 var can_inline = compressor.option("inline") && !self.is_expr_pure(compressor);
5815 if (can_inline && stat instanceof AST_Return) {
5816 var value = stat.value;
5817 if (exp === fn && (!value || value.is_constant_expression())) {
5818 var args = self.args.concat(value || make_node(AST_Undefined, self));
5819 return make_sequence(self, args).optimize(compressor);
5820 }
5821 }
5822 if (is_func) {
5823 var def, value, var_assigned = false;
5824 if (can_inline
5825 && !fn.uses_arguments
5826 && !fn.pinned()
5827 && !(fn.name && fn instanceof AST_Function)
5828 && (value = can_flatten_body(stat))
5829 && (exp === fn
5830 || !recursive_ref(compressor, def = exp.definition()) && fn.is_constant_expression(exp.scope))
5831 && !fn.contains_this()) {
5832 if (can_substitute_directly()) {
5833 var args = self.args.slice();
5834 args.push(value.clone(true).transform(new TreeTransformer(function(node) {
5835 if (node instanceof AST_SymbolRef) {
5836 var def = node.definition();
5837 if (fn.variables.get(node.name) !== def) {
5838 if (exp !== fn) def.references.push(node);
5839 return node;
5840 }
5841 var index = resolve_index(def);
5842 var arg = args[index];
5843 if (!arg) return make_node(AST_Undefined, self);
5844 args[index] = null;
5845 var parent = this.parent();
5846 return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
5847 }
5848 })));
5849 var node = make_sequence(self, args.filter(function(arg) {
5850 return arg;
5851 })).optimize(compressor);
5852 node = maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
5853 if (best_of(compressor, self, node) === node) return node;
5854 }
5855 var scope, in_loop, level = -1;
5856 if ((exp === fn || compressor.option("unused") && exp.definition().references.length == 1)
5857 && can_inject_symbols()) {
5858 fn._squeezed = true;
5859 if (exp !== fn) fn.parent_scope = exp.scope;
5860 var node = make_sequence(self, flatten_fn()).optimize(compressor);
5861 return maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
5862 }
5863 }
5864 if (compressor.option("side_effects")
5865 && all(fn.body, is_empty)
5866 && (fn !== exp || safe_to_drop(fn, compressor))) {
5867 var args = self.args.concat(make_node(AST_Undefined, self));
5868 return make_sequence(self, args).optimize(compressor);
5869 }
5870 }
5871 if (compressor.option("drop_console")) {
5872 if (exp instanceof AST_PropAccess) {
5873 var name = exp.expression;
5874 while (name.expression) {
5875 name = name.expression;
5876 }
5877 if (is_undeclared_ref(name) && name.name == "console") {
5878 return make_node(AST_Undefined, self).optimize(compressor);
5879 }
5880 }
5881 }
5882 if (compressor.option("negate_iife")
5883 && compressor.parent() instanceof AST_SimpleStatement
5884 && is_iife_call(self)) {
5885 return self.negate(compressor, true);
5886 }
5887 return try_evaluate(compressor, self);
5888
5889 function return_value(stat) {
5890 if (!stat) return make_node(AST_Undefined, self);
5891 if (stat instanceof AST_Return) {
5892 if (!stat.value) return make_node(AST_Undefined, self);
5893 return stat.value.clone(true);
5894 }
5895 if (stat instanceof AST_SimpleStatement) {
5896 return make_node(AST_UnaryPrefix, stat, {
5897 operator: "void",
5898 expression: stat.body
5899 });
5900 }
5901 }
5902
5903 function can_flatten_body(stat) {
5904 var len = fn.body.length;
5905 if (compressor.option("inline") < 3) {
5906 return len == 1 && return_value(stat);
5907 }
5908 stat = null;
5909 for (var i = 0; i < len; i++) {
5910 var line = fn.body[i];
5911 if (line instanceof AST_Var) {
5912 var assigned = var_assigned || !all(line.definitions, function(var_def) {
5913 return !var_def.value;
5914 });
5915 if (assigned) {
5916 var_assigned = true;
5917 if (stat) return false;
5918 }
5919 } else if (line instanceof AST_Defun || line instanceof AST_EmptyStatement) {
5920 continue;
5921 } else if (stat) {
5922 return false;
5923 } else {
5924 stat = line;
5925 }
5926 }
5927 return return_value(stat);
5928 }
5929
5930 function resolve_index(def) {
5931 for (var i = fn.argnames.length; --i >= 0;) {
5932 if (fn.argnames[i].definition() === def) return i;
5933 }
5934 }
5935
5936 function can_substitute_directly() {
5937 if (var_assigned) return;
5938 if (compressor.option("inline") <= 1 && fn.argnames.length) return;
5939 if (!fn.variables.all(function(def) {
5940 return def.references.length < 2 && def.orig[0] instanceof AST_SymbolFunarg;
5941 })) return;
5942 var abort = false;
5943 var begin;
5944 var in_order = [];
5945 var side_effects = false;
5946 value.walk(new TreeWalker(function(node) {
5947 if (abort) return true;
5948 if (node instanceof AST_Binary && lazy_op[node.operator]
5949 || node instanceof AST_Conditional) {
5950 in_order = null;
5951 return;
5952 }
5953 if (node instanceof AST_Scope) return abort = true;
5954 var def;
5955 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === (def = node.definition())) {
5956 if (def.init instanceof AST_Defun) return abort = true;
5957 if (is_lhs(node, this.parent())) return abort = true;
5958 var index = resolve_index(def);
5959 if (!(begin < index)) begin = index;
5960 if (!in_order) return;
5961 if (side_effects) {
5962 in_order = null;
5963 } else {
5964 in_order.push(fn.argnames[index]);
5965 }
5966 return;
5967 }
5968 if (node.has_side_effects(compressor)) side_effects = true;
5969 }));
5970 if (abort) return;
5971 var end = self.args.length;
5972 if (in_order && fn.argnames.length >= end) {
5973 end = fn.argnames.length;
5974 while (end-- > begin && fn.argnames[end] === in_order.pop());
5975 end++;
5976 }
5977 var scope = side_effects && compressor.find_parent(AST_Scope);
5978 return end <= begin || all(self.args.slice(begin, end), side_effects ? function(funarg) {
5979 return funarg.is_constant_expression(scope);
5980 } : function(funarg) {
5981 return !funarg.has_side_effects(compressor);
5982 });
5983 }
5984
5985 function var_exists(defined, name) {
5986 return defined[name] || identifier_atom[name] || scope.var_names()[name];
5987 }
5988
5989 function can_inject_args(catches, used, safe_to_inject) {
5990 for (var i = 0; i < fn.argnames.length; i++) {
5991 var arg = fn.argnames[i];
5992 if (arg.__unused) continue;
5993 if (!safe_to_inject || var_exists(catches, arg.name)) return false;
5994 used[arg.name] = true;
5995 if (in_loop) in_loop.push(arg.definition());
5996 }
5997 return true;
5998 }
5999
6000 function can_inject_vars(catches, used, safe_to_inject) {
6001 for (var i = 0; i < fn.body.length; i++) {
6002 var stat = fn.body[i];
6003 if (stat instanceof AST_Defun) {
6004 if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
6005 continue;
6006 }
6007 if (!(stat instanceof AST_Var)) continue;
6008 if (!safe_to_inject) return false;
6009 for (var j = stat.definitions.length; --j >= 0;) {
6010 var name = stat.definitions[j].name;
6011 if (var_exists(catches, name.name)) return false;
6012 if (in_loop) in_loop.push(name.definition());
6013 }
6014 }
6015 return true;
6016 }
6017
6018 function can_inject_symbols() {
6019 var catches = Object.create(null);
6020 do {
6021 scope = compressor.parent(++level);
6022 if (scope instanceof AST_Catch) {
6023 catches[scope.argname.name] = true;
6024 } else if (scope instanceof AST_IterationStatement) {
6025 in_loop = [];
6026 } else if (scope instanceof AST_SymbolRef) {
6027 if (scope.fixed_value() instanceof AST_Scope) return false;
6028 }
6029 } while (!(scope instanceof AST_Scope));
6030 var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
6031 && (exp !== fn || fn.parent_scope === compressor.find_parent(AST_Scope));
6032 var inline = compressor.option("inline");
6033 var used = Object.create(catches);
6034 if (!can_inject_args(catches, used, inline >= 2 && safe_to_inject)) return false;
6035 if (!can_inject_vars(catches, used, inline >= 3 && safe_to_inject)) return false;
6036 return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
6037 }
6038
6039 function append_var(decls, expressions, name, value) {
6040 var def = name.definition();
6041 scope.variables.set(name.name, def);
6042 scope.enclosed.push(def);
6043 if (!scope.var_names()[name.name]) {
6044 scope.var_names()[name.name] = true;
6045 decls.push(make_node(AST_VarDef, name, {
6046 name: name,
6047 value: null
6048 }));
6049 }
6050 var sym = make_node(AST_SymbolRef, name, name);
6051 def.references.push(sym);
6052 if (value) expressions.push(make_node(AST_Assign, self, {
6053 operator: "=",
6054 left: sym,
6055 right: value
6056 }));
6057 }
6058
6059 function flatten_args(decls, expressions) {
6060 var len = fn.argnames.length;
6061 for (var i = self.args.length; --i >= len;) {
6062 expressions.push(self.args[i]);
6063 }
6064 for (i = len; --i >= 0;) {
6065 var name = fn.argnames[i];
6066 var value = self.args[i];
6067 if (name.__unused || scope.var_names()[name.name]) {
6068 if (value) expressions.push(value);
6069 } else {
6070 var symbol = make_node(AST_SymbolVar, name, name);
6071 name.definition().orig.push(symbol);
6072 if (!value && in_loop) value = make_node(AST_Undefined, self);
6073 append_var(decls, expressions, symbol, value);
6074 }
6075 }
6076 decls.reverse();
6077 expressions.reverse();
6078 }
6079
6080 function flatten_vars(decls, expressions) {
6081 var pos = expressions.length;
6082 for (var i = 0; i < fn.body.length; i++) {
6083 var stat = fn.body[i];
6084 if (!(stat instanceof AST_Var)) continue;
6085 for (var j = 0; j < stat.definitions.length; j++) {
6086 var var_def = stat.definitions[j];
6087 var name = var_def.name;
6088 var redef = name.definition().redefined();
6089 if (redef) {
6090 name = name.clone();
6091 name.thedef = redef;
6092 }
6093 append_var(decls, expressions, name, var_def.value);
6094 if (in_loop && all(fn.argnames, function(argname) {
6095 return argname.name != name.name;
6096 })) {
6097 var def = fn.variables.get(name.name);
6098 var sym = make_node(AST_SymbolRef, name, name);
6099 def.references.push(sym);
6100 expressions.splice(pos++, 0, make_node(AST_Assign, var_def, {
6101 operator: "=",
6102 left: sym,
6103 right: make_node(AST_Undefined, name)
6104 }));
6105 }
6106 }
6107 }
6108 }
6109
6110 function flatten_fn() {
6111 var decls = [];
6112 var expressions = [];
6113 flatten_args(decls, expressions);
6114 flatten_vars(decls, expressions);
6115 expressions.push(value);
6116 var args = fn.body.filter(function(stat) {
6117 if (stat instanceof AST_Defun) {
6118 var def = stat.name.definition();
6119 scope.functions.set(def.name, def);
6120 scope.variables.set(def.name, def);
6121 scope.enclosed.push(def);
6122 scope.var_names()[def.name] = true;
6123 return true;
6124 }
6125 });
6126 args.unshift(scope.body.indexOf(compressor.parent(level - 1)) + 1, 0);
6127 if (decls.length) args.push(make_node(AST_Var, fn, {
6128 definitions: decls
6129 }));
6130 [].splice.apply(scope.body, args);
6131 fn.enclosed.forEach(function(def) {
6132 if (scope.var_names()[def.name]) return;
6133 scope.enclosed.push(def);
6134 scope.var_names()[def.name] = true;
6135 });
6136 return expressions;
6137 }
6138 });
6139
6140 OPT(AST_New, function(self, compressor) {
6141 if (compressor.option("sequences")) {
6142 var seq = lift_sequence_in_expression(self, compressor);
6143 if (seq !== self) return seq.optimize(compressor);
6144 }
6145 if (compressor.option("unsafe")) {
6146 var exp = self.expression;
6147 if (is_undeclared_ref(exp)) {
6148 switch (exp.name) {
6149 case "Object":
6150 case "RegExp":
6151 case "Function":
6152 case "Error":
6153 case "Array":
6154 return make_node(AST_Call, self, self).transform(compressor);
6155 }
6156 }
6157 }
6158 return self;
6159 });
6160
6161 OPT(AST_Sequence, function(self, compressor) {
6162 if (!compressor.option("side_effects")) return self;
6163 var expressions = [];
6164 filter_for_side_effects();
6165 var end = expressions.length - 1;
6166 trim_right_for_undefined();
6167 if (end == 0) {
6168 self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
6169 if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
6170 return self;
6171 }
6172 self.expressions = expressions;
6173 return self;
6174
6175 function filter_for_side_effects() {
6176 var first = first_in_statement(compressor);
6177 var last = self.expressions.length - 1;
6178 self.expressions.forEach(function(expr, index) {
6179 if (index < last) expr = expr.drop_side_effect_free(compressor, first);
6180 if (expr) {
6181 merge_sequence(expressions, expr);
6182 first = false;
6183 }
6184 });
6185 }
6186
6187 function trim_right_for_undefined() {
6188 while (end > 0 && is_undefined(expressions[end], compressor)) end--;
6189 if (end < expressions.length - 1) {
6190 expressions[end] = make_node(AST_UnaryPrefix, self, {
6191 operator : "void",
6192 expression : expressions[end]
6193 });
6194 expressions.length = end + 1;
6195 }
6196 }
6197 });
6198
6199 OPT(AST_UnaryPostfix, function(self, compressor) {
6200 if (compressor.option("sequences")) {
6201 var seq = lift_sequence_in_expression(self, compressor);
6202 if (seq !== self) return seq.optimize(compressor);
6203 }
6204 return self;
6205 });
6206
6207 var SIGN_OPS = makePredicate("+ -");
6208 var MULTIPLICATIVE_OPS = makePredicate("* / %");
6209 OPT(AST_UnaryPrefix, function(self, compressor) {
6210 var e = self.expression;
6211 if (compressor.option("evaluate")
6212 && self.operator == "delete"
6213 && !(e instanceof AST_SymbolRef
6214 || e instanceof AST_PropAccess
6215 || is_identifier_atom(e))) {
6216 if (e instanceof AST_Sequence) {
6217 e = e.expressions.slice();
6218 e.push(make_node(AST_True, self));
6219 return make_sequence(self, e).optimize(compressor);
6220 }
6221 return make_sequence(self, [ e, make_node(AST_True, self) ]).optimize(compressor);
6222 }
6223 if (compressor.option("sequences")) {
6224 var seq = lift_sequence_in_expression(self, compressor);
6225 if (seq !== self) return seq.optimize(compressor);
6226 }
6227 if (compressor.option("side_effects") && self.operator == "void") {
6228 e = e.drop_side_effect_free(compressor);
6229 if (e) {
6230 self.expression = e;
6231 return self;
6232 } else {
6233 return make_node(AST_Undefined, self).optimize(compressor);
6234 }
6235 }
6236 if (compressor.option("booleans")) {
6237 if (self.operator == "!" && e.is_truthy()) {
6238 return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor);
6239 } else if (compressor.in_boolean_context()) switch (self.operator) {
6240 case "!":
6241 if (e instanceof AST_UnaryPrefix && e.operator == "!") {
6242 // !!foo => foo, if we're in boolean context
6243 return e.expression;
6244 }
6245 if (e instanceof AST_Binary) {
6246 self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
6247 }
6248 break;
6249 case "typeof":
6250 // typeof always returns a non-empty string, thus it's
6251 // always true in booleans
6252 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
6253 return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
6254 e,
6255 make_node(AST_True, self)
6256 ])).optimize(compressor);
6257 }
6258 }
6259 if (self.operator == "-" && e instanceof AST_Infinity) e = e.transform(compressor);
6260 if (compressor.option("evaluate")
6261 && e instanceof AST_Binary
6262 && SIGN_OPS[self.operator]
6263 && MULTIPLICATIVE_OPS[e.operator]
6264 && (e.left.is_constant() || !e.right.has_side_effects(compressor))) {
6265 return make_node(AST_Binary, self, {
6266 operator: e.operator,
6267 left: make_node(AST_UnaryPrefix, e.left, {
6268 operator: self.operator,
6269 expression: e.left
6270 }),
6271 right: e.right
6272 });
6273 }
6274 // avoids infinite recursion of numerals
6275 return self.operator == "-" && (e instanceof AST_Number || e instanceof AST_Infinity)
6276 ? self : try_evaluate(compressor, self);
6277 });
6278
6279 AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
6280 if (this.left instanceof AST_PropAccess) {
6281 if (!(this.left.expression instanceof AST_Sequence)) return this;
6282 var x = this.left.expression.expressions.slice();
6283 var e = this.clone();
6284 e.left = e.left.clone();
6285 e.left.expression = x.pop();
6286 x.push(e);
6287 return make_sequence(this, x);
6288 }
6289 if (this.left instanceof AST_Sequence) {
6290 var x = this.left.expressions.slice();
6291 var e = this.clone();
6292 e.left = x.pop();
6293 x.push(e);
6294 return make_sequence(this, x);
6295 }
6296 if (this.right instanceof AST_Sequence) {
6297 if (this.left.has_side_effects(compressor)) return this;
6298 var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
6299 var x = this.right.expressions;
6300 var last = x.length - 1;
6301 for (var i = 0; i < last; i++) {
6302 if (!assign && x[i].has_side_effects(compressor)) break;
6303 }
6304 if (i == last) {
6305 x = x.slice();
6306 var e = this.clone();
6307 e.right = x.pop();
6308 x.push(e);
6309 return make_sequence(this, x);
6310 }
6311 if (i > 0) {
6312 var e = this.clone();
6313 e.right = make_sequence(this.right, x.slice(i));
6314 x = x.slice(0, i);
6315 x.push(e);
6316 return make_sequence(this, x);
6317 }
6318 }
6319 return this;
6320 });
6321
6322 var indexFns = makePredicate("indexOf lastIndexOf");
6323 var commutativeOperators = makePredicate("== === != !== * & | ^");
6324 function is_object(node) {
6325 return node instanceof AST_Array
6326 || node instanceof AST_Lambda
6327 || node instanceof AST_Object;
6328 }
6329
6330 OPT(AST_Binary, function(self, compressor) {
6331 function reversible() {
6332 return self.left.is_constant()
6333 || self.right.is_constant()
6334 || !self.left.has_side_effects(compressor)
6335 && !self.right.has_side_effects(compressor);
6336 }
6337 function reverse(op) {
6338 if (reversible()) {
6339 if (op) self.operator = op;
6340 var tmp = self.left;
6341 self.left = self.right;
6342 self.right = tmp;
6343 }
6344 }
6345 function swap_chain() {
6346 var rhs = self.right;
6347 self.left = make_node(AST_Binary, self, {
6348 operator: self.operator,
6349 left: self.left,
6350 right: rhs.left,
6351 start: self.left.start,
6352 end: rhs.left.end
6353 });
6354 self.right = rhs.right;
6355 self.left = self.left.transform(compressor);
6356 }
6357 if (commutativeOperators[self.operator]
6358 && self.right.is_constant()
6359 && !self.left.is_constant()
6360 && !(self.left instanceof AST_Binary
6361 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
6362 // if right is a constant, whatever side effects the
6363 // left side might have could not influence the
6364 // result. hence, force switch.
6365 reverse();
6366 }
6367 if (compressor.option("sequences")) {
6368 var seq = self.lift_sequences(compressor);
6369 if (seq !== self) return seq.optimize(compressor);
6370 }
6371 if (compressor.option("assignments") && lazy_op[self.operator]) {
6372 var assign = self.right;
6373 // a || (a = x) => a = a || x
6374 // a && (a = x) => a = a && x
6375 if (self.left instanceof AST_SymbolRef
6376 && assign instanceof AST_Assign
6377 && assign.operator == "="
6378 && self.left.equivalent_to(assign.left)) {
6379 self.right = assign.right;
6380 assign.right = self;
6381 return assign;
6382 }
6383 }
6384 if (compressor.option("comparisons")) switch (self.operator) {
6385 case "===":
6386 case "!==":
6387 if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
6388 AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
6389 return make_sequence(self, [
6390 self.right,
6391 make_node(self.operator == "===" ? AST_False : AST_True, self)
6392 ]).optimize(compressor);
6393 }
6394 var is_strict_comparison = true;
6395 if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
6396 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
6397 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
6398 self.left.equivalent_to(self.right)) {
6399 self.operator = self.operator.substr(0, 2);
6400 }
6401 // XXX: intentionally falling down to the next case
6402 case "==":
6403 case "!=":
6404 // void 0 == x => null == x
6405 if (!is_strict_comparison && is_undefined(self.left, compressor)) {
6406 self.left = make_node(AST_Null, self.left);
6407 }
6408 // "undefined" == typeof x => undefined === x
6409 else if (compressor.option("typeofs")
6410 && self.left instanceof AST_String
6411 && self.left.value == "undefined"
6412 && self.right instanceof AST_UnaryPrefix
6413 && self.right.operator == "typeof") {
6414 var expr = self.right.expression;
6415 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
6416 : !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
6417 self.right = expr;
6418 self.left = make_node(AST_Undefined, self.left).optimize(compressor);
6419 if (self.operator.length == 2) self.operator += "=";
6420 }
6421 }
6422 // obj !== obj => false
6423 else if (self.left instanceof AST_SymbolRef
6424 && self.right instanceof AST_SymbolRef
6425 && self.left.definition() === self.right.definition()
6426 && is_object(self.left.fixed_value())) {
6427 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
6428 }
6429 break;
6430 case "&&":
6431 case "||":
6432 // void 0 !== x && null !== x => null != x
6433 // void 0 === x || null === x => null == x
6434 var lhs = self.left;
6435 if (lhs.operator == self.operator) {
6436 lhs = lhs.right;
6437 }
6438 if (lhs instanceof AST_Binary
6439 && lhs.operator == (self.operator == "&&" ? "!==" : "===")
6440 && self.right instanceof AST_Binary
6441 && lhs.operator == self.right.operator
6442 && (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
6443 || lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
6444 && !lhs.right.has_side_effects(compressor)
6445 && lhs.right.equivalent_to(self.right.right)) {
6446 var combined = make_node(AST_Binary, self, {
6447 operator: lhs.operator.slice(0, -1),
6448 left: make_node(AST_Null, self),
6449 right: lhs.right
6450 });
6451 if (lhs !== self.left) {
6452 combined = make_node(AST_Binary, self, {
6453 operator: self.operator,
6454 left: self.left.left,
6455 right: combined
6456 });
6457 }
6458 return combined;
6459 }
6460 break;
6461 }
6462 var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
6463 if (in_bool) switch (self.operator) {
6464 case "+":
6465 var ll = self.left.evaluate(compressor);
6466 var rr = self.right.evaluate(compressor);
6467 if (ll && typeof ll == "string") {
6468 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
6469 return make_sequence(self, [
6470 self.right,
6471 make_node(AST_True, self)
6472 ]).optimize(compressor);
6473 }
6474 if (rr && typeof rr == "string") {
6475 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
6476 return make_sequence(self, [
6477 self.left,
6478 make_node(AST_True, self)
6479 ]).optimize(compressor);
6480 }
6481 break;
6482 case "==":
6483 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
6484 return make_node(AST_UnaryPrefix, self, {
6485 operator: "!",
6486 expression: self.right
6487 }).optimize(compressor);
6488 }
6489 break;
6490 case "!=":
6491 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
6492 return self.right.optimize(compressor);
6493 }
6494 break;
6495 }
6496 var parent = compressor.parent();
6497 if (compressor.option("comparisons") && self.is_boolean(compressor)) {
6498 if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
6499 var negated = make_node(AST_UnaryPrefix, self, {
6500 operator: "!",
6501 expression: self.negate(compressor, first_in_statement(compressor))
6502 });
6503 self = best_of(compressor, self, negated);
6504 }
6505 switch (self.operator) {
6506 case ">": reverse("<"); break;
6507 case ">=": reverse("<="); break;
6508 }
6509 }
6510 // x && (y && z) => x && y && z
6511 // x || (y || z) => x || y || z
6512 if (compressor.option("conditionals")
6513 && lazy_op[self.operator]
6514 && self.right instanceof AST_Binary
6515 && self.operator == self.right.operator) {
6516 swap_chain();
6517 }
6518 if (compressor.option("strings") && self.operator == "+") {
6519 // "foo" + 42 + "" => "foo" + 42
6520 if (self.right instanceof AST_String
6521 && self.right.value == ""
6522 && self.left.is_string(compressor)) {
6523 return self.left.optimize(compressor);
6524 }
6525 // "" + ("foo" + 42) => "foo" + 42
6526 if (self.left instanceof AST_String
6527 && self.left.value == ""
6528 && self.right.is_string(compressor)) {
6529 return self.right.optimize(compressor);
6530 }
6531 // "" + 42 + "foo" => 42 + "foo"
6532 if (self.left instanceof AST_Binary
6533 && self.left.operator == "+"
6534 && self.left.left instanceof AST_String
6535 && self.left.left.value == ""
6536 && self.right.is_string(compressor)) {
6537 self.left = self.left.right;
6538 return self.optimize(compressor);
6539 }
6540 // "x" + (y + "z") => "x" + y + "z"
6541 // x + ("y" + z) => x + "y" + z
6542 if (self.right instanceof AST_Binary
6543 && self.operator == self.right.operator
6544 && (self.left.is_string(compressor) && self.right.is_string(compressor)
6545 || self.right.left.is_string(compressor)
6546 && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
6547 swap_chain();
6548 }
6549 }
6550 if (compressor.option("evaluate")) {
6551 var associative = true;
6552 switch (self.operator) {
6553 case "&&":
6554 var ll = fuzzy_eval(self.left);
6555 if (!ll) {
6556 AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
6557 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
6558 } else if (!(ll instanceof AST_Node)) {
6559 AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
6560 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
6561 }
6562 var rr = self.right.evaluate(compressor);
6563 if (!rr) {
6564 if (in_bool) {
6565 AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
6566 return make_sequence(self, [
6567 self.left,
6568 make_node(AST_False, self)
6569 ]).optimize(compressor);
6570 } else self.falsy = true;
6571 } else if (!(rr instanceof AST_Node)) {
6572 if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
6573 AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
6574 return self.left.optimize(compressor);
6575 }
6576 }
6577 // (x || false) && y => x ? y : false
6578 if (self.left.operator == "||") {
6579 var lr = self.left.right.evaluate(compressor, true);
6580 if (!lr) return make_node(AST_Conditional, self, {
6581 condition: self.left.left,
6582 consequent: self.right,
6583 alternative: self.left.right
6584 }).optimize(compressor);
6585 }
6586 break;
6587 case "||":
6588 var ll = fuzzy_eval(self.left);
6589 if (!ll) {
6590 AST_Node.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
6591 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
6592 } else if (!(ll instanceof AST_Node)) {
6593 AST_Node.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
6594 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
6595 }
6596 var rr = self.right.evaluate(compressor);
6597 if (!rr) {
6598 if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
6599 AST_Node.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
6600 return self.left.optimize(compressor);
6601 }
6602 } else if (!(rr instanceof AST_Node)) {
6603 if (in_bool) {
6604 AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
6605 return make_sequence(self, [
6606 self.left,
6607 make_node(AST_True, self)
6608 ]).optimize(compressor);
6609 } else self.truthy = true;
6610 }
6611 // x && true || y => x ? true : y
6612 if (self.left.operator == "&&") {
6613 var lr = self.left.right.is_truthy() || self.left.right.evaluate(compressor, true);
6614 if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
6615 condition: self.left.left,
6616 consequent: self.left.right,
6617 alternative: self.right
6618 }).optimize(compressor);
6619 }
6620 break;
6621 case "+":
6622 // "foo" + ("bar" + x) => "foobar" + x
6623 if (self.left instanceof AST_Constant
6624 && self.right instanceof AST_Binary
6625 && self.right.operator == "+"
6626 && self.right.left instanceof AST_Constant
6627 && self.right.is_string(compressor)) {
6628 self = make_node(AST_Binary, self, {
6629 operator: "+",
6630 left: make_node(AST_String, self.left, {
6631 value: "" + self.left.value + self.right.left.value,
6632 start: self.left.start,
6633 end: self.right.left.end
6634 }),
6635 right: self.right.right
6636 });
6637 }
6638 // (x + "foo") + "bar" => x + "foobar"
6639 if (self.right instanceof AST_Constant
6640 && self.left instanceof AST_Binary
6641 && self.left.operator == "+"
6642 && self.left.right instanceof AST_Constant
6643 && self.left.is_string(compressor)) {
6644 self = make_node(AST_Binary, self, {
6645 operator: "+",
6646 left: self.left.left,
6647 right: make_node(AST_String, self.right, {
6648 value: "" + self.left.right.value + self.right.value,
6649 start: self.left.right.start,
6650 end: self.right.end
6651 })
6652 });
6653 }
6654 // (x + "foo") + ("bar" + y) => (x + "foobar") + y
6655 if (self.left instanceof AST_Binary
6656 && self.left.operator == "+"
6657 && self.left.is_string(compressor)
6658 && self.left.right instanceof AST_Constant
6659 && self.right instanceof AST_Binary
6660 && self.right.operator == "+"
6661 && self.right.left instanceof AST_Constant
6662 && self.right.is_string(compressor)) {
6663 self = make_node(AST_Binary, self, {
6664 operator: "+",
6665 left: make_node(AST_Binary, self.left, {
6666 operator: "+",
6667 left: self.left.left,
6668 right: make_node(AST_String, self.left.right, {
6669 value: "" + self.left.right.value + self.right.left.value,
6670 start: self.left.right.start,
6671 end: self.right.left.end
6672 })
6673 }),
6674 right: self.right.right
6675 });
6676 }
6677 // a + -b => a - b
6678 if (self.right instanceof AST_UnaryPrefix
6679 && self.right.operator == "-"
6680 && self.left.is_number(compressor)) {
6681 self = make_node(AST_Binary, self, {
6682 operator: "-",
6683 left: self.left,
6684 right: self.right.expression
6685 });
6686 break;
6687 }
6688 // -a + b => b - a
6689 if (self.left instanceof AST_UnaryPrefix
6690 && self.left.operator == "-"
6691 && reversible()
6692 && self.right.is_number(compressor)) {
6693 self = make_node(AST_Binary, self, {
6694 operator: "-",
6695 left: self.right,
6696 right: self.left.expression
6697 });
6698 break;
6699 }
6700 // (a + b) + 3 => 3 + (a + b)
6701 if (compressor.option("unsafe_math")
6702 && self.left instanceof AST_Binary
6703 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
6704 && self.right.is_constant()
6705 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
6706 && self.left.is_number(compressor)
6707 && !self.left.right.is_constant()
6708 && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
6709 self = make_node(AST_Binary, self, {
6710 operator: self.left.operator,
6711 left: make_node(AST_Binary, self, {
6712 operator: self.operator,
6713 left: self.right,
6714 right: self.left.left
6715 }),
6716 right: self.left.right
6717 });
6718 break;
6719 }
6720 case "-":
6721 // a - -b => a + b
6722 if (self.right instanceof AST_UnaryPrefix
6723 && self.right.operator == "-"
6724 && self.left.is_number(compressor)
6725 && self.right.expression.is_number(compressor)) {
6726 self = make_node(AST_Binary, self, {
6727 operator: "+",
6728 left: self.left,
6729 right: self.right.expression
6730 });
6731 break;
6732 }
6733 case "*":
6734 case "/":
6735 associative = compressor.option("unsafe_math");
6736 // +a - b => a - b
6737 // a - +b => a - b
6738 if (self.operator != "+") {
6739 if (self.left instanceof AST_UnaryPrefix && self.left.operator == "+") {
6740 self.left = self.left.expression;
6741 }
6742 if (self.right instanceof AST_UnaryPrefix && self.right.operator == "+") {
6743 self.right = self.right.expression;
6744 }
6745 }
6746 case "&":
6747 case "|":
6748 case "^":
6749 // a + +b => +b + a
6750 if (self.operator != "-"
6751 && self.operator != "/"
6752 && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
6753 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
6754 && reversible()
6755 && !(self.left instanceof AST_Binary
6756 && self.left.operator != self.operator
6757 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
6758 var reversed = make_node(AST_Binary, self, {
6759 operator: self.operator,
6760 left: self.right,
6761 right: self.left
6762 });
6763 if (self.right instanceof AST_Constant
6764 && !(self.left instanceof AST_Constant)) {
6765 self = best_of(compressor, reversed, self);
6766 } else {
6767 self = best_of(compressor, self, reversed);
6768 }
6769 }
6770 if (!associative || !self.is_number(compressor)) break;
6771 // a + (b + c) => (a + b) + c
6772 if (self.right instanceof AST_Binary
6773 && self.right.operator != "%"
6774 && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
6775 && self.right.is_number(compressor)
6776 && (self.operator != "+"
6777 || self.right.left.is_boolean(compressor)
6778 || self.right.left.is_number(compressor))
6779 && (self.operator != "-" || !self.left.is_negative_zero())
6780 && (self.right.left.is_constant_expression()
6781 || !self.right.right.has_side_effects(compressor))) {
6782 self = make_node(AST_Binary, self, {
6783 operator: align(self.operator, self.right.operator),
6784 left: make_node(AST_Binary, self.left, {
6785 operator: self.operator,
6786 left: self.left,
6787 right: self.right.left,
6788 start: self.left.start,
6789 end: self.right.left.end
6790 }),
6791 right: self.right.right
6792 });
6793 if (self.operator == "+"
6794 && !self.right.is_boolean(compressor)
6795 && !self.right.is_number(compressor)) {
6796 self.right = make_node(AST_UnaryPrefix, self.right, {
6797 operator: "+",
6798 expression: self.right
6799 });
6800 }
6801 }
6802 // (2 * n) * 3 => 6 * n
6803 // (n + 2) + 3 => n + 5
6804 if (self.right instanceof AST_Constant
6805 && self.left instanceof AST_Binary
6806 && self.left.operator != "%"
6807 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
6808 && self.left.is_number(compressor)) {
6809 if (self.left.left instanceof AST_Constant) {
6810 var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
6811 self = make_binary(self, self.left.operator, lhs, self.left.right);
6812 } else if (self.left.right instanceof AST_Constant) {
6813 var rhs = make_binary(self.left, align(self.left.operator, self.operator), self.left.right, self.right, self.left.right.start, self.right.end);
6814 if (self.left.operator != "-"
6815 || !self.right.value
6816 || rhs.evaluate(compressor)
6817 || !self.left.left.is_negative_zero()) {
6818 self = make_binary(self, self.left.operator, self.left.left, rhs);
6819 }
6820 }
6821 }
6822 break;
6823 }
6824 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
6825 // 0 + n => n
6826 case "+":
6827 if (self.left.value == 0) {
6828 if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
6829 operator: "+",
6830 expression: self.right
6831 }).optimize(compressor);
6832 if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
6833 }
6834 break;
6835 // 1 * n => n
6836 case "*":
6837 if (self.left.value == 1) {
6838 return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
6839 operator: "+",
6840 expression: self.right
6841 }).optimize(compressor);
6842 }
6843 break;
6844 }
6845 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
6846 // n + 0 => n
6847 case "+":
6848 if (self.right.value == 0) {
6849 if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
6850 operator: "+",
6851 expression: self.left
6852 }).optimize(compressor);
6853 if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
6854 }
6855 break;
6856 // n - 0 => n
6857 case "-":
6858 if (self.right.value == 0) {
6859 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
6860 operator: "+",
6861 expression: self.left
6862 }).optimize(compressor);
6863 }
6864 break;
6865 // n / 1 => n
6866 case "/":
6867 if (self.right.value == 1) {
6868 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
6869 operator: "+",
6870 expression: self.left
6871 }).optimize(compressor);
6872 }
6873 break;
6874 }
6875 }
6876 if (compressor.option("typeofs")) switch (self.operator) {
6877 case "&&":
6878 mark_locally_defined(self.left, self.right, null, "&&");
6879 break;
6880 case "||":
6881 mark_locally_defined(self.left, null, self.right, "||");
6882 break;
6883 }
6884 if (compressor.option("unsafe")) {
6885 var indexRight = is_indexFn(self.right);
6886 if (in_bool
6887 && indexRight
6888 && (self.operator == "==" || self.operator == "!=")
6889 && self.left instanceof AST_Number
6890 && self.left.value == 0) {
6891 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
6892 operator: "!",
6893 expression: self.right
6894 }) : self.right).optimize(compressor);
6895 }
6896 var indexLeft = is_indexFn(self.left);
6897 if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
6898 var node = make_node(AST_UnaryPrefix, self, {
6899 operator: "!",
6900 expression: make_node(AST_UnaryPrefix, self, {
6901 operator: "~",
6902 expression: indexLeft ? self.left : self.right
6903 })
6904 });
6905 switch (self.operator) {
6906 case "<":
6907 if (indexLeft) break;
6908 case "<=":
6909 case "!=":
6910 node = make_node(AST_UnaryPrefix, self, {
6911 operator: "!",
6912 expression: node
6913 });
6914 break;
6915 }
6916 return node.optimize(compressor);
6917 }
6918 }
6919 return try_evaluate(compressor, self);
6920
6921 function align(ref, op) {
6922 switch (ref) {
6923 case "-":
6924 return op == "+" ? "-" : "+";
6925 case "/":
6926 return op == "*" ? "/" : "*";
6927 default:
6928 return op;
6929 }
6930 }
6931
6932 function make_binary(orig, op, left, right, start, end) {
6933 if (op == "+") {
6934 if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
6935 left = make_node(AST_UnaryPrefix, left, {
6936 operator: "+",
6937 expression: left
6938 });
6939 }
6940 if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
6941 right = make_node(AST_UnaryPrefix, right, {
6942 operator: "+",
6943 expression: right
6944 });
6945 }
6946 }
6947 return make_node(AST_Binary, orig, {
6948 operator: op,
6949 left: left,
6950 right: right,
6951 start: start,
6952 end: end
6953 });
6954 }
6955
6956 function fuzzy_eval(node) {
6957 if (node.truthy) return true;
6958 if (node.falsy) return false;
6959 if (node.is_truthy()) return true;
6960 return node.evaluate(compressor, true);
6961 }
6962
6963 function is_indexFn(node) {
6964 return node instanceof AST_Call
6965 && node.expression instanceof AST_Dot
6966 && indexFns[node.expression.property];
6967 }
6968
6969 function is_indexOf_match_pattern() {
6970 switch (self.operator) {
6971 case "<=":
6972 // 0 <= array.indexOf(string) => !!~array.indexOf(string)
6973 return indexRight && self.left instanceof AST_Number && self.left.value == 0;
6974 case "<":
6975 // array.indexOf(string) < 0 => !~array.indexOf(string)
6976 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
6977 // -1 < array.indexOf(string) => !!~array.indexOf(string)
6978 case "==":
6979 case "!=":
6980 // -1 == array.indexOf(string) => !~array.indexOf(string)
6981 // -1 != array.indexOf(string) => !!~array.indexOf(string)
6982 if (!indexRight) return false;
6983 return self.left instanceof AST_Number && self.left.value == -1
6984 || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
6985 && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
6986 }
6987 }
6988 });
6989
6990 function recursive_ref(compressor, def) {
6991 var node;
6992 for (var i = 0; node = compressor.parent(i); i++) {
6993 if (node instanceof AST_Lambda) {
6994 var name = node.name;
6995 if (name && name.definition() === def) break;
6996 }
6997 }
6998 return node;
6999 }
7000
7001 OPT(AST_SymbolRef, function(self, compressor) {
7002 if (!compressor.option("ie8")
7003 && is_undeclared_ref(self)
7004 // testing against `self.scope.uses_with` is an optimization
7005 && !(self.scope.uses_with && compressor.find_parent(AST_With))) {
7006 switch (self.name) {
7007 case "undefined":
7008 return make_node(AST_Undefined, self).optimize(compressor);
7009 case "NaN":
7010 return make_node(AST_NaN, self).optimize(compressor);
7011 case "Infinity":
7012 return make_node(AST_Infinity, self).optimize(compressor);
7013 }
7014 }
7015 var parent = compressor.parent();
7016 if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
7017 var def = self.definition();
7018 var fixed = self.fixed_value();
7019 var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
7020 if (single_use) {
7021 if (fixed instanceof AST_Lambda) {
7022 if (def.scope !== self.scope
7023 && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
7024 single_use = false;
7025 } else if (recursive_ref(compressor, def)) {
7026 single_use = false;
7027 } else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
7028 single_use = fixed.is_constant_expression(self.scope);
7029 if (single_use == "f") {
7030 var scope = self.scope;
7031 do if (scope instanceof AST_Defun || scope instanceof AST_Function) {
7032 scope.inlined = true;
7033 } while (scope = scope.parent_scope);
7034 }
7035 }
7036 if (single_use) fixed.parent_scope = self.scope;
7037 } else if (!fixed || !fixed.is_constant_expression()) {
7038 single_use = false;
7039 }
7040 }
7041 if (single_use) {
7042 def.single_use = false;
7043 fixed._squeezed = true;
7044 fixed.single_use = true;
7045 if (fixed instanceof AST_Defun) {
7046 fixed = make_node(AST_Function, fixed, fixed);
7047 fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
7048 }
7049 if (fixed instanceof AST_Lambda) {
7050 var scope = self.scope;
7051 fixed.enclosed.forEach(function(def) {
7052 if (fixed.variables.has(def.name)) return;
7053 if (scope.var_names()[def.name]) return;
7054 scope.enclosed.push(def);
7055 scope.var_names()[def.name] = true;
7056 });
7057 }
7058 var value;
7059 if (def.recursive_refs > 0) {
7060 value = fixed.clone(true);
7061 var defun_def = value.name.definition();
7062 var lambda_def = value.variables.get(value.name.name);
7063 var name = lambda_def && lambda_def.orig[0];
7064 if (!(name instanceof AST_SymbolLambda)) {
7065 name = make_node(AST_SymbolLambda, value.name, value.name);
7066 name.scope = value;
7067 value.name = name;
7068 lambda_def = value.def_function(name);
7069 lambda_def.recursive_refs = def.recursive_refs;
7070 }
7071 value.walk(new TreeWalker(function(node) {
7072 if (!(node instanceof AST_SymbolRef)) return;
7073 var def = node.definition();
7074 if (def === defun_def) {
7075 node.thedef = lambda_def;
7076 lambda_def.references.push(node);
7077 } else {
7078 def.single_use = false;
7079 var fn = node.fixed_value();
7080 if (!(fn instanceof AST_Lambda)) return;
7081 if (!fn.name) return;
7082 if (fn.name.definition() !== def) return;
7083 if (def.scope !== fn.name.scope) return;
7084 if (fixed.variables.get(fn.name.name) !== def) return;
7085 fn.name = fn.name.clone();
7086 var value_def = value.variables.get(fn.name.name) || value.def_function(fn.name);
7087 node.thedef = value_def;
7088 value_def.references.push(node);
7089 }
7090 }));
7091 } else {
7092 value = fixed.optimize(compressor);
7093 }
7094 def.replaced++;
7095 return value;
7096 }
7097 if (fixed && def.should_replace === undefined) {
7098 var init;
7099 if (fixed instanceof AST_This) {
7100 if (!(def.orig[0] instanceof AST_SymbolFunarg) && same_scope(def)) {
7101 init = fixed;
7102 }
7103 } else {
7104 var ev = fixed.evaluate(compressor);
7105 if (ev !== fixed && (!(ev instanceof RegExp)
7106 || compressor.option("unsafe_regexp") && !def.cross_loop && same_scope(def))) {
7107 init = make_node_from_constant(ev, fixed);
7108 }
7109 }
7110 if (init) {
7111 var value_length = init.optimize(compressor).print_to_string().length;
7112 var fn;
7113 if (has_symbol_ref(fixed)) {
7114 fn = function() {
7115 var result = init.optimize(compressor);
7116 return result === init ? result.clone(true) : result;
7117 };
7118 } else {
7119 value_length = Math.min(value_length, fixed.print_to_string().length);
7120 fn = function() {
7121 var result = best_of_expression(init.optimize(compressor), fixed);
7122 return result === init || result === fixed ? result.clone(true) : result;
7123 };
7124 }
7125 var name_length = def.name.length;
7126 if (compressor.option("unused") && !compressor.exposed(def)) {
7127 name_length += (name_length + 2 + value_length) / (def.references.length - def.assignments);
7128 }
7129 var delta = value_length - Math.floor(name_length);
7130 def.should_replace = delta < compressor.eval_threshold ? fn : false;
7131 } else {
7132 def.should_replace = false;
7133 }
7134 }
7135 if (def.should_replace) {
7136 var value = def.should_replace();
7137 def.replaced++;
7138 return value;
7139 }
7140 }
7141 return self;
7142
7143 function same_scope(def) {
7144 var scope = def.scope.resolve();
7145 return all(def.references, function(ref) {
7146 return scope === ref.scope.resolve();
7147 });
7148 }
7149
7150 function has_symbol_ref(value) {
7151 var found;
7152 value.walk(new TreeWalker(function(node) {
7153 if (node instanceof AST_SymbolRef) found = true;
7154 if (found) return true;
7155 }));
7156 return found;
7157 }
7158 });
7159
7160 function is_atomic(lhs, self) {
7161 return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
7162 }
7163
7164 OPT(AST_Undefined, function(self, compressor) {
7165 if (compressor.option("unsafe_undefined")) {
7166 var undef = find_variable(compressor, "undefined");
7167 if (undef) {
7168 var ref = make_node(AST_SymbolRef, self, {
7169 name : "undefined",
7170 scope : undef.scope,
7171 thedef : undef
7172 });
7173 ref.is_undefined = true;
7174 return ref;
7175 }
7176 }
7177 var lhs = is_lhs(compressor.self(), compressor.parent());
7178 if (lhs && is_atomic(lhs, self)) return self;
7179 return make_node(AST_UnaryPrefix, self, {
7180 operator: "void",
7181 expression: make_node(AST_Number, self, {
7182 value: 0
7183 })
7184 });
7185 });
7186
7187 OPT(AST_Infinity, function(self, compressor) {
7188 var lhs = is_lhs(compressor.self(), compressor.parent());
7189 if (lhs && is_atomic(lhs, self)) return self;
7190 if (compressor.option("keep_infinity")
7191 && !(lhs && !is_atomic(lhs, self))
7192 && !find_variable(compressor, "Infinity"))
7193 return self;
7194 return make_node(AST_Binary, self, {
7195 operator: "/",
7196 left: make_node(AST_Number, self, {
7197 value: 1
7198 }),
7199 right: make_node(AST_Number, self, {
7200 value: 0
7201 })
7202 });
7203 });
7204
7205 OPT(AST_NaN, function(self, compressor) {
7206 var lhs = is_lhs(compressor.self(), compressor.parent());
7207 if (lhs && !is_atomic(lhs, self)
7208 || find_variable(compressor, "NaN")) {
7209 return make_node(AST_Binary, self, {
7210 operator: "/",
7211 left: make_node(AST_Number, self, {
7212 value: 0
7213 }),
7214 right: make_node(AST_Number, self, {
7215 value: 0
7216 })
7217 });
7218 }
7219 return self;
7220 });
7221
7222 function is_reachable(self, defs) {
7223 var reachable = false;
7224 var find_ref = new TreeWalker(function(node) {
7225 if (reachable) return true;
7226 if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
7227 return reachable = true;
7228 }
7229 });
7230 var scan_scope = new TreeWalker(function(node) {
7231 if (reachable) return true;
7232 if (node instanceof AST_Scope && node !== self) {
7233 var parent = scan_scope.parent();
7234 if (parent instanceof AST_Call && parent.expression === node) return;
7235 node.walk(find_ref);
7236 return true;
7237 }
7238 });
7239 self.walk(scan_scope);
7240 return reachable;
7241 }
7242
7243 var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
7244 var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
7245 OPT(AST_Assign, function(self, compressor) {
7246 if (compressor.option("dead_code")) {
7247 if (self.left instanceof AST_PropAccess) {
7248 if (self.operator == "=") {
7249 var exp = self.left.expression;
7250 if (exp instanceof AST_Lambda
7251 || !compressor.has_directive("use strict")
7252 && exp instanceof AST_Constant
7253 && !exp.may_throw_on_access(compressor)) {
7254 return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
7255 self.left.property,
7256 self.right
7257 ]).optimize(compressor);
7258 }
7259 }
7260 } else if (self.left instanceof AST_SymbolRef) {
7261 if (self.left.is_immutable()) return strip_assignment();
7262 var def = self.left.definition();
7263 var scope = def.scope.resolve();
7264 var local = scope === compressor.find_parent(AST_Lambda);
7265 var level = 0, node, parent = self;
7266 do {
7267 node = parent;
7268 parent = compressor.parent(level++);
7269 if (parent instanceof AST_Assign) {
7270 if (!(parent.left instanceof AST_SymbolRef)) continue;
7271 if (parent.left.definition() !== def) continue;
7272 if (in_try(level, parent)) break;
7273 def.fixed = false;
7274 return strip_assignment();
7275 } else if (parent instanceof AST_Exit) {
7276 if (!local) break;
7277 if (in_try(level, parent)) break;
7278 if (is_reachable(scope, [ def ])) break;
7279 def.fixed = false;
7280 return strip_assignment();
7281 }
7282 } while (parent instanceof AST_Binary && parent.right === node
7283 || parent instanceof AST_Sequence && parent.tail_node() === node
7284 || parent instanceof AST_UnaryPrefix);
7285 }
7286 }
7287 if (compressor.option("sequences")) {
7288 var seq = self.lift_sequences(compressor);
7289 if (seq !== self) return seq.optimize(compressor);
7290 }
7291 if (!compressor.option("assignments")) return self;
7292 if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
7293 // x = expr1 OP expr2
7294 if (self.right.left instanceof AST_SymbolRef
7295 && self.right.left.name == self.left.name
7296 && ASSIGN_OPS[self.right.operator]) {
7297 // x = x - 2 => x -= 2
7298 self.operator = self.right.operator + "=";
7299 self.right = self.right.right;
7300 }
7301 else if (self.right.right instanceof AST_SymbolRef
7302 && self.right.right.name == self.left.name
7303 && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
7304 && !self.right.left.has_side_effects(compressor)) {
7305 // x = 2 & x => x &= 2
7306 self.operator = self.right.operator + "=";
7307 self.right = self.right.left;
7308 }
7309 }
7310 if ((self.operator == "-=" || self.operator == "+="
7311 && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
7312 && self.right instanceof AST_Number
7313 && self.right.value == 1) {
7314 var op = self.operator.slice(0, -1);
7315 return make_node(AST_UnaryPrefix, self, {
7316 operator: op + op,
7317 expression: self.left
7318 });
7319 }
7320 return self;
7321
7322 function in_try(level, node) {
7323 var right = self.right;
7324 self.right = make_node(AST_Null, right);
7325 var may_throw = node.may_throw(compressor);
7326 self.right = right;
7327 var parent;
7328 while (parent = compressor.parent(level++)) {
7329 if (parent === scope) return false;
7330 if (parent instanceof AST_Try) {
7331 if (parent.bfinally) return true;
7332 if (may_throw && parent.bcatch) return true;
7333 }
7334 }
7335 }
7336
7337 function strip_assignment() {
7338 return (self.operator != "=" ? make_node(AST_Binary, self, {
7339 operator: self.operator.slice(0, -1),
7340 left: self.left,
7341 right: self.right
7342 }) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
7343 }
7344 });
7345
7346 OPT(AST_Conditional, function(self, compressor) {
7347 if (!compressor.option("conditionals")) return self;
7348 // This looks like lift_sequences(), should probably be under "sequences"
7349 if (self.condition instanceof AST_Sequence) {
7350 var expressions = self.condition.expressions.slice();
7351 self.condition = expressions.pop();
7352 expressions.push(self);
7353 return make_sequence(self, expressions);
7354 }
7355 var condition = self.condition.is_truthy() || self.condition.evaluate(compressor);
7356 if (!condition) {
7357 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
7358 return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
7359 } else if (!(condition instanceof AST_Node)) {
7360 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
7361 return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
7362 }
7363 var negated = condition.negate(compressor, first_in_statement(compressor));
7364 if (best_of(compressor, condition, negated) === negated) {
7365 self = make_node(AST_Conditional, self, {
7366 condition: negated,
7367 consequent: self.alternative,
7368 alternative: self.consequent
7369 });
7370 negated = condition;
7371 condition = self.condition;
7372 }
7373 var consequent = self.consequent;
7374 var alternative = self.alternative;
7375 // x ? x : y => x || y
7376 if (condition instanceof AST_SymbolRef
7377 && consequent instanceof AST_SymbolRef
7378 && condition.definition() === consequent.definition()) {
7379 return make_node(AST_Binary, self, {
7380 operator: "||",
7381 left: condition,
7382 right: alternative
7383 });
7384 }
7385 // if (foo) exp = something; else exp = something_else;
7386 // |
7387 // v
7388 // exp = foo ? something : something_else;
7389 var seq_tail = consequent.tail_node();
7390 if (seq_tail instanceof AST_Assign) {
7391 var is_eq = seq_tail.operator == "=";
7392 var alt_tail = is_eq ? alternative.tail_node() : alternative;
7393 if ((is_eq || consequent === seq_tail)
7394 && alt_tail instanceof AST_Assign
7395 && seq_tail.operator == alt_tail.operator
7396 && seq_tail.left.equivalent_to(alt_tail.left)
7397 && (is_eq && seq_tail.left instanceof AST_SymbolRef
7398 || !condition.has_side_effects(compressor)
7399 && can_shift_lhs_of_tail(consequent)
7400 && can_shift_lhs_of_tail(alternative))) {
7401 return make_node(AST_Assign, self, {
7402 operator: seq_tail.operator,
7403 left: seq_tail.left,
7404 right: make_node(AST_Conditional, self, {
7405 condition: condition,
7406 consequent: pop_lhs(consequent),
7407 alternative: pop_lhs(alternative)
7408 })
7409 });
7410 }
7411 }
7412 // x ? y : y => x, y
7413 if (consequent.equivalent_to(alternative)) return make_sequence(self, [
7414 condition,
7415 consequent
7416 ]).optimize(compressor);
7417 if (consequent instanceof AST_Call
7418 && alternative.TYPE === consequent.TYPE
7419 && consequent.args.length == alternative.args.length) {
7420 var arg_index = arg_diff();
7421 // x ? y(a) : z(a) => (x ? y : z)(a)
7422 if (arg_index == -1
7423 && !(consequent.expression instanceof AST_PropAccess)
7424 && !(alternative.expression instanceof AST_PropAccess)) {
7425 var node = consequent.clone();
7426 node.expression = make_node(AST_Conditional, self, {
7427 condition: condition,
7428 consequent: consequent.expression,
7429 alternative: alternative.expression
7430 });
7431 return node;
7432 }
7433 // x ? y(a) : y(b) => y(x ? a : b)
7434 if (arg_index >= 0
7435 && consequent.expression.equivalent_to(alternative.expression)
7436 && !condition.has_side_effects(compressor)
7437 && !consequent.expression.has_side_effects(compressor)) {
7438 var node = consequent.clone();
7439 node.args[arg_index] = make_node(AST_Conditional, self, {
7440 condition: condition,
7441 consequent: consequent.args[arg_index],
7442 alternative: alternative.args[arg_index]
7443 });
7444 return node;
7445 }
7446 }
7447 // x ? (y ? a : b) : b => x && y ? a : b
7448 if (consequent instanceof AST_Conditional
7449 && consequent.alternative.equivalent_to(alternative)) {
7450 return make_node(AST_Conditional, self, {
7451 condition: make_node(AST_Binary, self, {
7452 left: condition,
7453 operator: "&&",
7454 right: consequent.condition
7455 }),
7456 consequent: consequent.consequent,
7457 alternative: alternative
7458 });
7459 }
7460 // x ? (y ? a : b) : a => !x || y ? a : b
7461 if (consequent instanceof AST_Conditional
7462 && consequent.consequent.equivalent_to(alternative)) {
7463 return make_node(AST_Conditional, self, {
7464 condition: make_node(AST_Binary, self, {
7465 left: negated,
7466 operator: "||",
7467 right: consequent.condition
7468 }),
7469 consequent: alternative,
7470 alternative: consequent.alternative
7471 });
7472 }
7473 // x ? a : (y ? a : b) => x || y ? a : b
7474 if (alternative instanceof AST_Conditional
7475 && consequent.equivalent_to(alternative.consequent)) {
7476 return make_node(AST_Conditional, self, {
7477 condition: make_node(AST_Binary, self, {
7478 left: condition,
7479 operator: "||",
7480 right: alternative.condition
7481 }),
7482 consequent: consequent,
7483 alternative: alternative.alternative
7484 });
7485 }
7486 // x ? b : (y ? a : b) => !x && y ? a : b
7487 if (alternative instanceof AST_Conditional
7488 && consequent.equivalent_to(alternative.alternative)) {
7489 return make_node(AST_Conditional, self, {
7490 condition: make_node(AST_Binary, self, {
7491 left: negated,
7492 operator: "&&",
7493 right: alternative.condition
7494 }),
7495 consequent: alternative.consequent,
7496 alternative: consequent
7497 });
7498 }
7499 // x ? (a, c) : (b, c) => x ? a : b, c
7500 if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
7501 && consequent.tail_node().equivalent_to(alternative.tail_node())) {
7502 return make_sequence(self, [
7503 make_node(AST_Conditional, self, {
7504 condition: condition,
7505 consequent: pop_seq(consequent),
7506 alternative: pop_seq(alternative)
7507 }),
7508 consequent.tail_node()
7509 ]).optimize(compressor);
7510 }
7511 // x ? y && a : a => (!x || y) && a
7512 if (consequent instanceof AST_Binary
7513 && consequent.operator == "&&"
7514 && consequent.right.equivalent_to(alternative)) {
7515 return make_node(AST_Binary, self, {
7516 operator: "&&",
7517 left: make_node(AST_Binary, self, {
7518 operator: "||",
7519 left: negated,
7520 right: consequent.left
7521 }),
7522 right: alternative
7523 }).optimize(compressor);
7524 }
7525 // x ? y || a : a => x && y || a
7526 if (consequent instanceof AST_Binary
7527 && consequent.operator == "||"
7528 && consequent.right.equivalent_to(alternative)) {
7529 return make_node(AST_Binary, self, {
7530 operator: "||",
7531 left: make_node(AST_Binary, self, {
7532 operator: "&&",
7533 left: condition,
7534 right: consequent.left
7535 }),
7536 right: alternative
7537 }).optimize(compressor);
7538 }
7539 // x ? a : y && a => (x || y) && a
7540 if (alternative instanceof AST_Binary
7541 && alternative.operator == "&&"
7542 && alternative.right.equivalent_to(consequent)) {
7543 return make_node(AST_Binary, self, {
7544 operator: "&&",
7545 left: make_node(AST_Binary, self, {
7546 operator: "||",
7547 left: condition,
7548 right: alternative.left
7549 }),
7550 right: consequent
7551 }).optimize(compressor);
7552 }
7553 // x ? a : y || a => !x && y || a
7554 if (alternative instanceof AST_Binary
7555 && alternative.operator == "||"
7556 && alternative.right.equivalent_to(consequent)) {
7557 return make_node(AST_Binary, self, {
7558 operator: "||",
7559 left: make_node(AST_Binary, self, {
7560 operator: "&&",
7561 left: negated,
7562 right: alternative.left
7563 }),
7564 right: consequent
7565 }).optimize(compressor);
7566 }
7567 var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
7568 if (is_true(consequent)) {
7569 if (is_false(alternative)) {
7570 // c ? true : false => !!c
7571 return booleanize(condition);
7572 }
7573 // c ? true : x => !!c || x
7574 return make_node(AST_Binary, self, {
7575 operator: "||",
7576 left: booleanize(condition),
7577 right: alternative
7578 });
7579 }
7580 if (is_false(consequent)) {
7581 if (is_true(alternative)) {
7582 // c ? false : true => !c
7583 return booleanize(condition.negate(compressor));
7584 }
7585 // c ? false : x => !c && x
7586 return make_node(AST_Binary, self, {
7587 operator: "&&",
7588 left: booleanize(condition.negate(compressor)),
7589 right: alternative
7590 });
7591 }
7592 if (is_true(alternative)) {
7593 // c ? x : true => !c || x
7594 return make_node(AST_Binary, self, {
7595 operator: "||",
7596 left: booleanize(condition.negate(compressor)),
7597 right: consequent
7598 });
7599 }
7600 if (is_false(alternative)) {
7601 // c ? x : false => !!c && x
7602 return make_node(AST_Binary, self, {
7603 operator: "&&",
7604 left: booleanize(condition),
7605 right: consequent
7606 });
7607 }
7608 if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
7609 return self;
7610
7611 function booleanize(node) {
7612 if (node.is_boolean(compressor)) return node;
7613 // !!expression
7614 return make_node(AST_UnaryPrefix, node, {
7615 operator: "!",
7616 expression: node.negate(compressor)
7617 });
7618 }
7619
7620 // AST_True or !0
7621 function is_true(node) {
7622 return node instanceof AST_True
7623 || in_bool
7624 && node instanceof AST_Constant
7625 && node.value
7626 || (node instanceof AST_UnaryPrefix
7627 && node.operator == "!"
7628 && node.expression instanceof AST_Constant
7629 && !node.expression.value);
7630 }
7631 // AST_False or !1 or void 0
7632 function is_false(node) {
7633 return node instanceof AST_False
7634 || in_bool
7635 && (node instanceof AST_Constant
7636 && !node.value
7637 || node instanceof AST_UnaryPrefix
7638 && node.operator == "void"
7639 && !node.expression.has_side_effects(compressor))
7640 || (node instanceof AST_UnaryPrefix
7641 && node.operator == "!"
7642 && node.expression instanceof AST_Constant
7643 && node.expression.value);
7644 }
7645
7646 function arg_diff() {
7647 var a = consequent.args;
7648 var b = alternative.args;
7649 for (var i = 0, len = a.length; i < len; i++) {
7650 if (!a[i].equivalent_to(b[i])) {
7651 for (var j = i + 1; j < len; j++) {
7652 if (!a[j].equivalent_to(b[j])) return -2;
7653 }
7654 return i;
7655 }
7656 }
7657 return -1;
7658 }
7659
7660 function can_shift_lhs_of_tail(node) {
7661 return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
7662 return !expr.has_side_effects(compressor);
7663 });
7664 }
7665
7666 function pop_lhs(node) {
7667 if (!(node instanceof AST_Sequence)) return node.right;
7668 var exprs = node.expressions.slice();
7669 exprs.push(exprs.pop().right);
7670 return make_sequence(node, exprs);
7671 }
7672
7673 function pop_seq(node) {
7674 if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
7675 value: 0
7676 });
7677 return make_sequence(node, node.expressions.slice(0, -1));
7678 }
7679 });
7680
7681 OPT(AST_Boolean, function(self, compressor) {
7682 if (!compressor.option("booleans")) return self;
7683 if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
7684 value: +self.value
7685 });
7686 var p = compressor.parent();
7687 if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
7688 AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
7689 operator : p.operator,
7690 value : self.value,
7691 file : p.start.file,
7692 line : p.start.line,
7693 col : p.start.col,
7694 });
7695 return make_node(AST_Number, self, {
7696 value: +self.value
7697 });
7698 }
7699 return make_node(AST_UnaryPrefix, self, {
7700 operator: "!",
7701 expression: make_node(AST_Number, self, {
7702 value: 1 - self.value
7703 })
7704 });
7705 });
7706
7707 function safe_to_flatten(value, compressor) {
7708 if (value instanceof AST_SymbolRef) {
7709 value = value.fixed_value();
7710 }
7711 if (!value) return false;
7712 if (!(value instanceof AST_Lambda)) return true;
7713 var parent = compressor.parent();
7714 if (parent.TYPE != "Call") return true;
7715 if (parent.expression !== compressor.self()) return true;
7716 return !value.contains_this();
7717 }
7718
7719 OPT(AST_Sub, function(self, compressor) {
7720 var expr = self.expression;
7721 var prop = self.property;
7722 if (compressor.option("properties")) {
7723 var key = prop.evaluate(compressor);
7724 if (key !== prop) {
7725 if (typeof key == "string") {
7726 if (key == "undefined") {
7727 key = undefined;
7728 } else {
7729 var value = parseFloat(key);
7730 if (value.toString() == key) {
7731 key = value;
7732 }
7733 }
7734 }
7735 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
7736 var property = "" + key;
7737 if (is_identifier_string(property)
7738 && property.length <= prop.print_to_string().length + 1) {
7739 return make_node(AST_Dot, self, {
7740 expression: expr,
7741 property: property
7742 }).optimize(compressor);
7743 }
7744 }
7745 }
7746 var parent = compressor.parent();
7747 var def, fn, fn_parent;
7748 if (compressor.option("arguments")
7749 && expr instanceof AST_SymbolRef
7750 && is_arguments(def = expr.definition())
7751 && prop instanceof AST_Number
7752 && (fn = expr.scope) === find_lambda()) {
7753 var index = prop.value;
7754 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
7755 if (!def.deleted) def.deleted = [];
7756 def.deleted[index] = true;
7757 }
7758 var argname = fn.argnames[index];
7759 if (def.deleted && def.deleted[index]) {
7760 argname = null;
7761 } else if (argname && compressor.has_directive("use strict")) {
7762 var arg_def = argname.definition();
7763 if (!compressor.option("reduce_vars")
7764 || def.reassigned
7765 || arg_def.assignments
7766 || arg_def.orig.length > 1) {
7767 argname = null;
7768 }
7769 } else if (!argname && index < fn.argnames.length + 5 && compressor.drop_fargs(fn, fn_parent)) {
7770 while (index >= fn.argnames.length) {
7771 argname = make_node(AST_SymbolFunarg, fn, {
7772 name: fn.make_var_name("argument_" + fn.argnames.length),
7773 scope: fn
7774 });
7775 fn.argnames.push(argname);
7776 fn.enclosed.push(fn.def_variable(argname));
7777 }
7778 }
7779 if (argname && find_if(function(node) {
7780 return node.name === argname.name;
7781 }, fn.argnames) === argname) {
7782 def.reassigned = false;
7783 var sym = make_node(AST_SymbolRef, self, argname);
7784 sym.reference({});
7785 delete argname.__unused;
7786 return sym;
7787 }
7788 }
7789 if (is_lhs(compressor.self(), parent)) return self;
7790 if (compressor.option("sequences") && compressor.parent().TYPE != "Call") {
7791 var seq = lift_sequence_in_expression(self, compressor);
7792 if (seq !== self) return seq.optimize(compressor);
7793 }
7794 if (key !== prop) {
7795 var sub = self.flatten_object(property, compressor);
7796 if (sub) {
7797 expr = self.expression = sub.expression;
7798 prop = self.property = sub.property;
7799 }
7800 }
7801 if (compressor.option("properties") && compressor.option("side_effects")
7802 && prop instanceof AST_Number && expr instanceof AST_Array) {
7803 var index = prop.value;
7804 var elements = expr.elements;
7805 var retValue = elements[index];
7806 if (safe_to_flatten(retValue, compressor)) {
7807 var flatten = true;
7808 var values = [];
7809 for (var i = elements.length; --i > index;) {
7810 var value = elements[i].drop_side_effect_free(compressor);
7811 if (value) {
7812 values.unshift(value);
7813 if (flatten && value.has_side_effects(compressor)) flatten = false;
7814 }
7815 }
7816 retValue = retValue instanceof AST_Hole ? make_node(AST_Undefined, retValue) : retValue;
7817 if (!flatten) values.unshift(retValue);
7818 while (--i >= 0) {
7819 var value = elements[i].drop_side_effect_free(compressor);
7820 if (value) values.unshift(value);
7821 else index--;
7822 }
7823 if (flatten) {
7824 values.push(retValue);
7825 return make_sequence(self, values).optimize(compressor);
7826 } else return make_node(AST_Sub, self, {
7827 expression: make_node(AST_Array, expr, {
7828 elements: values
7829 }),
7830 property: make_node(AST_Number, prop, {
7831 value: index
7832 })
7833 });
7834 }
7835 }
7836 return try_evaluate(compressor, self);
7837
7838 function find_lambda() {
7839 var i = 0, p;
7840 while (p = compressor.parent(i++)) {
7841 if (p instanceof AST_Lambda) {
7842 fn_parent = compressor.parent(i);
7843 return p;
7844 }
7845 }
7846 }
7847 });
7848
7849 AST_Scope.DEFMETHOD("contains_this", function() {
7850 var result;
7851 var self = this;
7852 self.walk(new TreeWalker(function(node) {
7853 if (result) return true;
7854 if (node instanceof AST_This) return result = true;
7855 if (node !== self && node instanceof AST_Scope) return true;
7856 }));
7857 return result;
7858 });
7859
7860 AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
7861 if (!compressor.option("properties")) return;
7862 var expr = this.expression;
7863 if (expr instanceof AST_Object) {
7864 var props = expr.properties;
7865 for (var i = props.length; --i >= 0;) {
7866 var prop = props[i];
7867 if ("" + prop.key == key) {
7868 if (!all(props, function(prop) {
7869 return prop instanceof AST_ObjectKeyVal;
7870 })) break;
7871 if (!safe_to_flatten(prop.value, compressor)) break;
7872 return make_node(AST_Sub, this, {
7873 expression: make_node(AST_Array, expr, {
7874 elements: props.map(function(prop) {
7875 return prop.value;
7876 })
7877 }),
7878 property: make_node(AST_Number, this, {
7879 value: i
7880 })
7881 });
7882 }
7883 }
7884 }
7885 });
7886
7887 OPT(AST_Dot, function(self, compressor) {
7888 if (self.property == "arguments" || self.property == "caller") {
7889 AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
7890 prop: self.property,
7891 file: self.start.file,
7892 line: self.start.line,
7893 col: self.start.col
7894 });
7895 }
7896 if (is_lhs(compressor.self(), compressor.parent())) return self;
7897 if (compressor.option("sequences") && compressor.parent().TYPE != "Call") {
7898 var seq = lift_sequence_in_expression(self, compressor);
7899 if (seq !== self) return seq.optimize(compressor);
7900 }
7901 if (compressor.option("unsafe_proto")
7902 && self.expression instanceof AST_Dot
7903 && self.expression.property == "prototype") {
7904 var exp = self.expression.expression;
7905 if (is_undeclared_ref(exp)) switch (exp.name) {
7906 case "Array":
7907 self.expression = make_node(AST_Array, self.expression, {
7908 elements: []
7909 });
7910 break;
7911 case "Function":
7912 self.expression = make_node(AST_Function, self.expression, {
7913 argnames: [],
7914 body: []
7915 });
7916 break;
7917 case "Number":
7918 self.expression = make_node(AST_Number, self.expression, {
7919 value: 0
7920 });
7921 break;
7922 case "Object":
7923 self.expression = make_node(AST_Object, self.expression, {
7924 properties: []
7925 });
7926 break;
7927 case "RegExp":
7928 self.expression = make_node(AST_RegExp, self.expression, {
7929 value: /t/
7930 });
7931 break;
7932 case "String":
7933 self.expression = make_node(AST_String, self.expression, {
7934 value: ""
7935 });
7936 break;
7937 }
7938 }
7939 var sub = self.flatten_object(self.property, compressor);
7940 if (sub) return sub.optimize(compressor);
7941 return try_evaluate(compressor, self);
7942 });
7943
7944 OPT(AST_Object, function(self, compressor) {
7945 if (!compressor.option("objects") || compressor.has_directive("use strict")) return self;
7946 var keys = new Dictionary();
7947 var values = [];
7948 self.properties.forEach(function(prop) {
7949 if (typeof prop.key != "string") {
7950 flush();
7951 values.push(prop);
7952 return;
7953 }
7954 if (prop.value.has_side_effects(compressor)) {
7955 flush();
7956 }
7957 keys.add(prop.key, prop.value);
7958 });
7959 flush();
7960 if (self.properties.length != values.length) {
7961 return make_node(AST_Object, self, {
7962 properties: values
7963 });
7964 }
7965 return self;
7966
7967 function flush() {
7968 keys.each(function(expressions, key) {
7969 values.push(make_node(AST_ObjectKeyVal, self, {
7970 key: key,
7971 value: make_sequence(self, expressions)
7972 }));
7973 });
7974 keys = new Dictionary();
7975 }
7976 });
7977
7978 OPT(AST_Return, function(self, compressor) {
7979 if (self.value && is_undefined(self.value, compressor)) {
7980 self.value = null;
7981 }
7982 return self;
7983 });
7984})(function(node, optimizer) {
7985 node.DEFMETHOD("optimize", function(compressor) {
7986 var self = this;
7987 if (self._optimized) return self;
7988 if (compressor.has_directive("use asm")) return self;
7989 var opt = optimizer(self, compressor);
7990 opt._optimized = true;
7991 return opt;
7992 });
7993});