UNPKG

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