1/*********************************************************************** 2 3 A JavaScript tokenizer / parser / beautifier / compressor. 4 https://github.com/mishoo/UglifyJS 5 6 -------------------------------- (C) --------------------------------- 7 8 Author: Mihai Bazon 9 <mihai.bazon@gmail.com> 10 http://mihai.bazon.net/blog 11 12 Distributed under the BSD license: 13 14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> 15 16 Redistribution and use in source and binary forms, with or without 17 modification, are permitted provided that the following conditions 18 are met: 19 20 * Redistributions of source code must retain the above 21 copyright notice, this list of conditions and the following 22 disclaimer. 23 24 * Redistributions in binary form must reproduce the above 25 copyright notice, this list of conditions and the following 26 disclaimer in the documentation and/or other materials 27 provided with the distribution. 28 29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 SUCH DAMAGE. 41 42 ***********************************************************************/ 43 44"use strict"; 45 46function Compressor(options, false_by_default) { 47 if (!(this instanceof Compressor)) 48 return new Compressor(options, false_by_default); 49 TreeTransformer.call(this, this.before, this.after); 50 this.options = defaults(options, { 51 annotations : !false_by_default, 52 arguments : !false_by_default, 53 arrows : !false_by_default, 54 assignments : !false_by_default, 55 awaits : !false_by_default, 56 booleans : !false_by_default, 57 collapse_vars : !false_by_default, 58 comparisons : !false_by_default, 59 conditionals : !false_by_default, 60 dead_code : !false_by_default, 61 default_values : !false_by_default, 62 directives : !false_by_default, 63 drop_console : false, 64 drop_debugger : !false_by_default, 65 evaluate : !false_by_default, 66 expression : false, 67 functions : !false_by_default, 68 global_defs : false, 69 hoist_exports : !false_by_default, 70 hoist_funs : false, 71 hoist_props : !false_by_default, 72 hoist_vars : false, 73 ie : false, 74 if_return : !false_by_default, 75 imports : !false_by_default, 76 inline : !false_by_default, 77 join_vars : !false_by_default, 78 keep_fargs : false_by_default, 79 keep_fnames : false, 80 keep_infinity : false, 81 loops : !false_by_default, 82 merge_vars : !false_by_default, 83 module : false, 84 negate_iife : !false_by_default, 85 objects : !false_by_default, 86 optional_chains : !false_by_default, 87 passes : 1, 88 properties : !false_by_default, 89 pure_funcs : null, 90 pure_getters : !false_by_default && "strict", 91 reduce_funcs : !false_by_default, 92 reduce_vars : !false_by_default, 93 rests : !false_by_default, 94 sequences : !false_by_default, 95 side_effects : !false_by_default, 96 spreads : !false_by_default, 97 strings : !false_by_default, 98 switches : !false_by_default, 99 templates : !false_by_default, 100 top_retain : null, 101 toplevel : !!(options && (options["module"] || options["top_retain"])), 102 typeofs : !false_by_default, 103 unsafe : false, 104 unsafe_comps : false, 105 unsafe_Function : false, 106 unsafe_math : false, 107 unsafe_proto : false, 108 unsafe_regexp : false, 109 unsafe_undefined: false, 110 unused : !false_by_default, 111 varify : !false_by_default, 112 webkit : false, 113 yields : !false_by_default, 114 }, true); 115 var evaluate = this.options["evaluate"]; 116 this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate; 117 var global_defs = this.options["global_defs"]; 118 if (typeof global_defs == "object") for (var key in global_defs) { 119 if (/^@/.test(key) && HOP(global_defs, key)) { 120 global_defs[key.slice(1)] = parse(global_defs[key], { expression: true }); 121 } 122 } 123 if (this.options["inline"] === true) this.options["inline"] = 4; 124 this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) { 125 if (lambda.length_read) return false; 126 var name = lambda.name; 127 if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda; 128 if (name.fixed_value() !== lambda) return false; 129 var def = name.definition(); 130 if (def.direct_access) return false; 131 var escaped = def.escaped; 132 return escaped && escaped.depth != 1; 133 }; 134 if (this.options["module"]) this.directives["use strict"] = true; 135 var pure_funcs = this.options["pure_funcs"]; 136 if (typeof pure_funcs == "function") { 137 this.pure_funcs = pure_funcs; 138 } else if (typeof pure_funcs == "string") { 139 this.pure_funcs = function(node) { 140 var expr; 141 if (node instanceof AST_Call) { 142 expr = node.expression; 143 } else if (node instanceof AST_Template) { 144 expr = node.tag; 145 } 146 return !(expr && pure_funcs === expr.print_to_string()); 147 }; 148 } else if (Array.isArray(pure_funcs)) { 149 this.pure_funcs = function(node) { 150 var expr; 151 if (node instanceof AST_Call) { 152 expr = node.expression; 153 } else if (node instanceof AST_Template) { 154 expr = node.tag; 155 } 156 return !(expr && member(expr.print_to_string(), pure_funcs)); 157 }; 158 } else { 159 this.pure_funcs = return_true; 160 } 161 var sequences = this.options["sequences"]; 162 this.sequences_limit = sequences == 1 ? 800 : sequences | 0; 163 var top_retain = this.options["top_retain"]; 164 if (top_retain instanceof RegExp) { 165 this.top_retain = function(def) { 166 return top_retain.test(def.name); 167 }; 168 } else if (typeof top_retain == "function") { 169 this.top_retain = top_retain; 170 } else if (top_retain) { 171 if (typeof top_retain == "string") { 172 top_retain = top_retain.split(/,/); 173 } 174 this.top_retain = function(def) { 175 return member(def.name, top_retain); 176 }; 177 } 178 var toplevel = this.options["toplevel"]; 179 this.toplevel = typeof toplevel == "string" ? { 180 funcs: /funcs/.test(toplevel), 181 vars: /vars/.test(toplevel) 182 } : { 183 funcs: toplevel, 184 vars: toplevel 185 }; 186} 187 188Compressor.prototype = new TreeTransformer(function(node, descend, in_list) { 189 if (node._squeezed) return node; 190 var is_scope = node instanceof AST_Scope; 191 if (is_scope) { 192 if (this.option("arrows") && is_arrow(node) && node.value) { 193 node.body = [ node.first_statement() ]; 194 node.value = null; 195 } 196 node.hoist_properties(this); 197 node.hoist_declarations(this); 198 node.process_returns(this); 199 } 200 // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize() 201 // would call AST_Node.transform() if a different instance of AST_Node is 202 // produced after OPT(). 203 // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction. 204 // Migrate and defer all children's AST_Node.transform() to below, which 205 // will now happen after this parent AST_Node has been properly substituted 206 // thus gives a consistent AST snapshot. 207 descend(node, this); 208 // Existing code relies on how AST_Node.optimize() worked, and omitting the 209 // following replacement call would result in degraded efficiency of both 210 // output and performance. 211 descend(node, this); 212 var opt = node.optimize(this); 213 if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) { 214 opt.drop_unused(this); 215 if (opt.merge_variables(this)) opt.drop_unused(this); 216 descend(opt, this); 217 } 218 if (opt === node) opt._squeezed = true; 219 return opt; 220}); 221Compressor.prototype.option = function(key) { 222 return this.options[key]; 223}; 224Compressor.prototype.exposed = function(def) { 225 if (def.exported) return true; 226 if (def.undeclared) return true; 227 if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false; 228 var toplevel = this.toplevel; 229 return !all(def.orig, function(sym) { 230 return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"]; 231 }); 232}; 233Compressor.prototype.compress = function(node) { 234 node = node.resolve_defines(this); 235 node.hoist_exports(this); 236 if (this.option("expression")) node.process_expression(true); 237 var merge_vars = this.options.merge_vars; 238 var passes = +this.options.passes || 1; 239 var min_count = 1 / 0; 240 var stopping = false; 241 var mangle = { ie: this.option("ie") }; 242 for (var pass = 0; pass < passes; pass++) { 243 node.figure_out_scope(mangle); 244 if (pass > 0 || this.option("reduce_vars")) 245 node.reset_opt_flags(this); 246 this.options.merge_vars = merge_vars && (stopping || pass == passes - 1); 247 node = node.transform(this); 248 if (passes > 1) { 249 var count = 0; 250 node.walk(new TreeWalker(function() { 251 count++; 252 })); 253 AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", { 254 pass: pass, 255 min_count: min_count, 256 count: count, 257 }); 258 if (count < min_count) { 259 min_count = count; 260 stopping = false; 261 } else if (stopping) { 262 break; 263 } else { 264 stopping = true; 265 } 266 } 267 } 268 if (this.option("expression")) node.process_expression(false); 269 return node; 270}; 271 272(function(OPT) { 273 OPT(AST_Node, function(self, compressor) { 274 return self; 275 }); 276 277 AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) { 278 if (!compressor.option("hoist_exports")) return; 279 var body = this.body, props = []; 280 for (var i = 0; i < body.length; i++) { 281 var stat = body[i]; 282 if (stat instanceof AST_ExportDeclaration) { 283 body[i] = stat = stat.body; 284 if (stat instanceof AST_Definitions) { 285 stat.definitions.forEach(function(defn) { 286 defn.name.match_symbol(export_symbol, true); 287 }); 288 } else { 289 export_symbol(stat.name); 290 } 291 } else if (stat instanceof AST_ExportReferences) { 292 body.splice(i--, 1); 293 [].push.apply(props, stat.properties); 294 } 295 } 296 if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props })); 297 298 function export_symbol(sym) { 299 if (!(sym instanceof AST_SymbolDeclaration)) return; 300 var node = make_node(AST_SymbolExport, sym); 301 node.alias = make_node(AST_String, node, { value: node.name }); 302 props.push(node); 303 } 304 }); 305 306 AST_Scope.DEFMETHOD("process_expression", function(insert, transform) { 307 var self = this; 308 var tt = new TreeTransformer(function(node) { 309 if (insert) { 310 if (node instanceof AST_Directive) node = make_node(AST_SimpleStatement, node, { 311 body: make_node(AST_String, node), 312 }); 313 if (node instanceof AST_SimpleStatement) { 314 return transform ? transform(node) : make_node(AST_Return, node, { value: node.body }); 315 } 316 } else if (node instanceof AST_Return) { 317 if (transform) return transform(node); 318 var value = node.value; 319 if (value instanceof AST_String) return make_node(AST_Directive, value); 320 return make_node(AST_SimpleStatement, node, { 321 body: value || make_node(AST_UnaryPrefix, node, { 322 operator: "void", 323 expression: make_node(AST_Number, node, { value: 0 }), 324 }), 325 }); 326 } 327 if (node instanceof AST_Block) { 328 if (node instanceof AST_Lambda) { 329 if (node !== self) return node; 330 } else if (insert === "awaits" && node instanceof AST_Try) { 331 if (node.bfinally) return node; 332 } 333 for (var index = node.body.length; --index >= 0;) { 334 var stat = node.body[index]; 335 if (!is_declaration(stat, true)) { 336 node.body[index] = stat.transform(tt); 337 break; 338 } 339 } 340 } else if (node instanceof AST_If) { 341 node.body = node.body.transform(tt); 342 if (node.alternative) node.alternative = node.alternative.transform(tt); 343 } else if (node instanceof AST_With) { 344 node.body = node.body.transform(tt); 345 } 346 return node; 347 }); 348 self.transform(tt); 349 }); 350 AST_Toplevel.DEFMETHOD("unwrap_expression", function() { 351 var self = this; 352 switch (self.body.length) { 353 case 0: 354 return make_node(AST_UnaryPrefix, self, { 355 operator: "void", 356 expression: make_node(AST_Number, self, { value: 0 }), 357 }); 358 case 1: 359 var stat = self.body[0]; 360 if (stat instanceof AST_Directive) return make_node(AST_String, stat); 361 if (stat instanceof AST_SimpleStatement) return stat.body; 362 default: 363 return make_node(AST_Call, self, { 364 expression: make_node(AST_Function, self, { 365 argnames: [], 366 body: self.body, 367 }).init_vars(self), 368 args: [], 369 }); 370 } 371 }); 372 AST_Node.DEFMETHOD("wrap_expression", function() { 373 var self = this; 374 if (!is_statement(self)) self = make_node(AST_SimpleStatement, self, { body: self }); 375 if (!(self instanceof AST_Toplevel)) self = make_node(AST_Toplevel, self, { body: [ self ] }); 376 return self; 377 }); 378 379 function read_property(obj, node) { 380 var key = node.get_property(); 381 if (key instanceof AST_Node) return; 382 var value; 383 if (obj instanceof AST_Array) { 384 var elements = obj.elements; 385 if (key == "length") return make_node_from_constant(elements.length, obj); 386 if (typeof key == "number" && key in elements) value = elements[key]; 387 } else if (obj instanceof AST_Lambda) { 388 if (key == "length") { 389 obj.length_read = true; 390 return make_node_from_constant(obj.argnames.length, obj); 391 } 392 } else if (obj instanceof AST_Object) { 393 key = "" + key; 394 var props = obj.properties; 395 for (var i = props.length; --i >= 0;) { 396 var prop = props[i]; 397 if (!can_hoist_property(prop)) return; 398 if (!value && props[i].key === key) value = props[i].value; 399 } 400 } 401 return value instanceof AST_SymbolRef && value.fixed_value() || value; 402 } 403 404 function is_read_only_fn(value, name) { 405 if (value instanceof AST_Boolean) return native_fns.Boolean[name]; 406 if (value instanceof AST_Number) return native_fns.Number[name]; 407 if (value instanceof AST_String) return native_fns.String[name]; 408 if (name == "valueOf") return false; 409 if (value instanceof AST_Array) return native_fns.Array[name]; 410 if (value instanceof AST_Lambda) return native_fns.Function[name]; 411 if (value instanceof AST_Object) return native_fns.Object[name]; 412 if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global; 413 } 414 415 function is_modified(compressor, tw, node, value, level, immutable, recursive) { 416 var parent = tw.parent(level); 417 if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) { 418 return; 419 } 420 var lhs = is_lhs(node, parent); 421 if (lhs) return lhs; 422 if (level == 0 && value && value.is_constant()) return; 423 if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1); 424 if (parent instanceof AST_Assign) switch (parent.operator) { 425 case "=": 426 return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive); 427 case "&&=": 428 case "||=": 429 case "??=": 430 return is_modified(compressor, tw, parent, parent, level + 1); 431 default: 432 return; 433 } 434 if (parent instanceof AST_Binary) { 435 if (!lazy_op[parent.operator]) return; 436 return is_modified(compressor, tw, parent, parent, level + 1); 437 } 438 if (parent instanceof AST_Call) { 439 return !immutable 440 && parent.expression === node 441 && !parent.is_expr_pure(compressor) 442 && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this()); 443 } 444 if (parent instanceof AST_Conditional) { 445 if (parent.condition === node) return; 446 return is_modified(compressor, tw, parent, parent, level + 1); 447 } 448 if (parent instanceof AST_ForEnumeration) return parent.init === node; 449 if (parent instanceof AST_ObjectKeyVal) { 450 if (parent.value !== node) return; 451 var obj = tw.parent(level + 1); 452 return is_modified(compressor, tw, obj, obj, level + 2); 453 } 454 if (parent instanceof AST_PropAccess) { 455 if (parent.expression !== node) return; 456 var prop = read_property(value, parent); 457 return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1); 458 } 459 if (parent instanceof AST_Sequence) { 460 if (parent.tail_node() !== node) return; 461 return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive); 462 } 463 } 464 465 function is_lambda(node) { 466 return node instanceof AST_Class || node instanceof AST_Lambda; 467 } 468 469 function safe_for_extends(node) { 470 return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function; 471 } 472 473 function is_arguments(def) { 474 return def.name == "arguments" && def.scope.uses_arguments; 475 } 476 477 function cross_scope(def, sym) { 478 do { 479 if (def === sym) return false; 480 if (sym instanceof AST_Scope) return true; 481 } while (sym = sym.parent_scope); 482 } 483 484 function can_drop_symbol(ref, compressor, keep_lambda) { 485 var def = ref.redef || ref.definition(); 486 if (ref.in_arg && is_funarg(def)) return false; 487 return all(def.orig, function(sym) { 488 if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) { 489 if (sym instanceof AST_SymbolImport) return true; 490 return compressor && can_varify(compressor, sym); 491 } 492 return !(keep_lambda && sym instanceof AST_SymbolLambda); 493 }); 494 } 495 496 function has_escaped(d, scope, node, parent) { 497 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node; 498 if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New; 499 if (parent instanceof AST_ClassField) return parent.value === node && !parent.static; 500 if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve(); 501 if (parent instanceof AST_VarDef) return parent.value === node; 502 } 503 504 function make_ref(ref, fixed) { 505 var node = make_node(AST_SymbolRef, ref); 506 node.fixed = fixed || make_node(AST_Undefined, ref); 507 return node; 508 } 509 510 function replace_ref(resolve, fixed) { 511 return function(node) { 512 var ref = resolve(node); 513 var node = make_ref(ref, fixed); 514 var def = ref.definition(); 515 def.references.push(node); 516 def.replaced++; 517 return node; 518 }; 519 } 520 521 var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/; 522 (function(def) { 523 def(AST_Node, noop); 524 525 function reset_def(tw, compressor, def) { 526 def.assignments = 0; 527 def.bool_return = 0; 528 def.drop_return = 0; 529 def.cross_loop = false; 530 def.direct_access = false; 531 def.escaped = []; 532 def.fixed = !def.const_redefs 533 && !def.scope.pinned() 534 && !compressor.exposed(def) 535 && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope) 536 && def.init; 537 def.reassigned = 0; 538 def.recursive_refs = 0; 539 def.references = []; 540 def.single_use = undefined; 541 } 542 543 function reset_block_variables(tw, compressor, scope) { 544 scope.variables.each(function(def) { 545 reset_def(tw, compressor, def); 546 }); 547 } 548 549 function reset_variables(tw, compressor, scope) { 550 scope.fn_defs = []; 551 scope.variables.each(function(def) { 552 reset_def(tw, compressor, def); 553 var init = def.init; 554 if (init instanceof AST_LambdaDefinition) { 555 scope.fn_defs.push(init); 556 init.safe_ids = null; 557 } 558 if (def.fixed === null) { 559 def.safe_ids = tw.safe_ids; 560 mark(tw, def); 561 } else if (def.fixed) { 562 tw.loop_ids[def.id] = tw.in_loop; 563 mark(tw, def); 564 } 565 }); 566 scope.may_call_this = function() { 567 scope.may_call_this = scope.contains_this() ? return_true : return_false; 568 }; 569 if (scope.uses_arguments) scope.each_argname(function(node) { 570 node.definition().last_ref = false; 571 }); 572 if (compressor.option("ie")) scope.variables.each(function(def) { 573 var d = def.orig[0].definition(); 574 if (d !== def) d.fixed = false; 575 }); 576 } 577 578 function safe_to_visit(tw, fn) { 579 var marker = fn.safe_ids; 580 return marker === undefined || marker === tw.safe_ids; 581 } 582 583 function walk_fn_def(tw, fn) { 584 var was_scanning = tw.fn_scanning; 585 tw.fn_scanning = fn; 586 fn.walk(tw); 587 tw.fn_scanning = was_scanning; 588 } 589 590 function revisit_fn_def(tw, fn) { 591 fn.enclosed.forEach(function(d) { 592 if (fn.variables.get(d.name) === d) return; 593 if (safe_to_read(tw, d)) return; 594 d.single_use = false; 595 var fixed = d.fixed; 596 if (typeof fixed == "function") fixed = fixed(); 597 if (fixed instanceof AST_Lambda && fixed.safe_ids !== undefined) return; 598 d.fixed = false; 599 }); 600 } 601 602 function mark_fn_def(tw, def, fn) { 603 var marker = fn.safe_ids; 604 if (marker === undefined) return; 605 if (marker === false) return; 606 if (fn.parent_scope.resolve().may_call_this === return_true) { 607 if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn); 608 } else if (marker) { 609 var visited = member(fn, tw.fn_visited); 610 if (marker === tw.safe_ids) { 611 if (!visited) walk_fn_def(tw, fn); 612 } else if (visited) { 613 revisit_fn_def(tw, fn); 614 } else { 615 fn.safe_ids = false; 616 } 617 } else if (tw.fn_scanning && tw.fn_scanning !== def.scope.resolve()) { 618 fn.safe_ids = false; 619 } else { 620 fn.safe_ids = tw.safe_ids; 621 walk_fn_def(tw, fn); 622 } 623 } 624 625 function pop_scope(tw, scope) { 626 var fn_defs = scope.fn_defs; 627 var tangled = scope.may_call_this === return_true ? fn_defs : fn_defs.filter(function(fn) { 628 if (fn.safe_ids === false) return true; 629 fn.safe_ids = tw.safe_ids; 630 walk_fn_def(tw, fn); 631 return false; 632 }); 633 pop(tw); 634 tangled.forEach(function(fn) { 635 fn.safe_ids = tw.safe_ids; 636 walk_fn_def(tw, fn); 637 }); 638 fn_defs.forEach(function(fn) { 639 fn.safe_ids = undefined; 640 }); 641 scope.fn_defs = undefined; 642 scope.may_call_this = undefined; 643 } 644 645 function push(tw, sequential) { 646 var safe_ids = Object.create(tw.safe_ids); 647 if (!sequential) safe_ids.seq = {}; 648 tw.safe_ids = safe_ids; 649 } 650 651 function pop(tw) { 652 tw.safe_ids = Object.getPrototypeOf(tw.safe_ids); 653 } 654 655 function mark(tw, def) { 656 tw.safe_ids[def.id] = {}; 657 } 658 659 function push_ref(def, ref) { 660 def.references.push(ref); 661 if (def.last_ref !== false) def.last_ref = ref; 662 } 663 664 function safe_to_read(tw, def) { 665 if (def.single_use == "m") return false; 666 var safe = tw.safe_ids[def.id]; 667 if (safe) { 668 var in_order = HOP(tw.safe_ids, def.id); 669 if (!in_order) { 670 var seq = tw.safe_ids.seq; 671 if (!safe.read) { 672 safe.read = seq; 673 } else if (safe.read !== seq) { 674 safe.read = true; 675 } 676 } 677 if (def.fixed == null) { 678 if (is_arguments(def)) return false; 679 if (def.global && def.name == "arguments") return false; 680 tw.loop_ids[def.id] = null; 681 def.fixed = make_node(AST_Undefined, def.orig[0]); 682 if (in_order) def.safe_ids = undefined; 683 return true; 684 } 685 return !safe.assign || safe.assign === tw.safe_ids; 686 } 687 return def.fixed instanceof AST_LambdaDefinition; 688 } 689 690 function safe_to_assign(tw, def, declare) { 691 if (!declare) { 692 if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false; 693 if (!all(def.orig, function(sym) { 694 return !(sym instanceof AST_SymbolConst); 695 })) return false; 696 } 697 if (def.fixed === undefined) return declare || all(def.orig, function(sym) { 698 return !(sym instanceof AST_SymbolLet); 699 }); 700 if (def.fixed === false || def.fixed === 0) return false; 701 var safe = tw.safe_ids[def.id]; 702 if (def.safe_ids) { 703 def.safe_ids[def.id] = false; 704 def.safe_ids = undefined; 705 return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read; 706 } 707 if (!HOP(tw.safe_ids, def.id)) { 708 if (!safe) return false; 709 if (safe.read || tw.in_loop) { 710 var scope = tw.find_parent(AST_BlockScope); 711 if (scope instanceof AST_Class) return false; 712 if (def.scope.resolve() !== scope.resolve()) return false; 713 } 714 safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids; 715 } 716 if (def.fixed != null && safe.read) { 717 if (safe.read !== tw.safe_ids.seq) return false; 718 if (tw.loop_ids[def.id] !== tw.in_loop) return false; 719 } 720 return safe_to_read(tw, def) && all(def.orig, function(sym) { 721 return !(sym instanceof AST_SymbolLambda); 722 }); 723 } 724 725 function ref_once(compressor, def) { 726 return compressor.option("unused") 727 && !def.scope.pinned() 728 && def.single_use !== false 729 && def.references.length - def.recursive_refs == 1 730 && !(is_funarg(def) && def.scope.uses_arguments); 731 } 732 733 function is_immutable(value) { 734 if (!value) return false; 735 if (value instanceof AST_Assign) { 736 var op = value.operator; 737 return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)]; 738 } 739 if (value instanceof AST_Sequence) return is_immutable(value.tail_node()); 740 return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity; 741 } 742 743 function value_in_use(node, parent) { 744 if (parent instanceof AST_Array) return true; 745 if (parent instanceof AST_Binary) return lazy_op[parent.operator]; 746 if (parent instanceof AST_Conditional) return parent.condition !== node; 747 if (parent instanceof AST_Sequence) return parent.tail_node() === node; 748 if (parent instanceof AST_Spread) return true; 749 } 750 751 function mark_escaped(tw, d, scope, node, value, level, depth) { 752 var parent = tw.parent(level); 753 if (value && value.is_constant()) return; 754 if (has_escaped(d, scope, node, parent)) { 755 d.escaped.push(parent); 756 if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1; 757 if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth; 758 if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true; 759 if (d.fixed) d.fixed.escaped = d.escaped; 760 return; 761 } else if (value_in_use(node, parent)) { 762 mark_escaped(tw, d, scope, parent, parent, level + 1, depth); 763 } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) { 764 var obj = tw.parent(level + 1); 765 mark_escaped(tw, d, scope, obj, obj, level + 2, depth); 766 } else if (parent instanceof AST_PropAccess && parent.expression === node) { 767 value = read_property(value, parent); 768 mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1); 769 if (value) return; 770 } 771 if (level > 0) return; 772 if (parent instanceof AST_Call && parent.expression === node) return; 773 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return; 774 if (parent instanceof AST_SimpleStatement) return; 775 if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return; 776 d.direct_access = true; 777 if (d.fixed) d.fixed.direct_access = true; 778 } 779 780 function mark_assignment_to_arguments(node) { 781 if (!(node instanceof AST_Sub)) return; 782 var expr = node.expression; 783 if (!(expr instanceof AST_SymbolRef)) return; 784 var def = expr.definition(); 785 if (!is_arguments(def)) return; 786 var key = node.property; 787 if (key.is_constant()) key = key.value; 788 if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return; 789 def.reassigned++; 790 (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) { 791 if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false; 792 }); 793 } 794 795 function make_fixed(save, fn) { 796 var prev_save, prev_value; 797 return function() { 798 var current = save(); 799 if (prev_save !== current) { 800 prev_save = current; 801 prev_value = fn(current); 802 } 803 return prev_value; 804 }; 805 } 806 807 function make_fixed_default(compressor, node, save) { 808 var prev_save, prev_seq; 809 return function() { 810 if (prev_seq === node) return node; 811 var current = save(); 812 var ev = fuzzy_eval(compressor, current, true); 813 if (ev instanceof AST_Node) { 814 prev_seq = node; 815 } else if (prev_save !== current) { 816 prev_save = current; 817 prev_seq = ev === undefined ? make_sequence(node, [ current, node.value ]) : current; 818 } 819 return prev_seq; 820 }; 821 } 822 823 function scan_declaration(tw, compressor, lhs, fixed, visit) { 824 var scanner = new TreeWalker(function(node) { 825 if (node instanceof AST_DefaultValue) { 826 reset_flags(node); 827 push(tw, true); 828 node.value.walk(tw); 829 pop(tw); 830 var save = fixed; 831 if (save) fixed = make_fixed_default(compressor, node, save); 832 node.name.walk(scanner); 833 fixed = save; 834 return true; 835 } 836 if (node instanceof AST_DestructuredArray) { 837 reset_flags(node); 838 var save = fixed; 839 node.elements.forEach(function(node, index) { 840 if (node instanceof AST_Hole) return reset_flags(node); 841 if (save) fixed = make_fixed(save, function(value) { 842 return make_node(AST_Sub, node, { 843 expression: value, 844 property: make_node(AST_Number, node, { value: index }), 845 }); 846 }); 847 node.walk(scanner); 848 }); 849 if (node.rest) { 850 var fixed_node; 851 if (save) fixed = compressor.option("rests") && make_fixed(save, function(value) { 852 if (!(value instanceof AST_Array)) return node; 853 for (var i = 0, len = node.elements.length; i < len; i++) { 854 if (value.elements[i] instanceof AST_Spread) return node; 855 } 856 if (!fixed_node) fixed_node = make_node(AST_Array, node, {}); 857 fixed_node.elements = value.elements.slice(len); 858 return fixed_node; 859 }); 860 node.rest.walk(scanner); 861 } 862 fixed = save; 863 return true; 864 } 865 if (node instanceof AST_DestructuredObject) { 866 reset_flags(node); 867 var save = fixed; 868 node.properties.forEach(function(node) { 869 reset_flags(node); 870 if (node.key instanceof AST_Node) { 871 push(tw); 872 node.key.walk(tw); 873 pop(tw); 874 } 875 if (save) fixed = make_fixed(save, function(value) { 876 var key = node.key; 877 var type = AST_Sub; 878 if (typeof key == "string") { 879 if (is_identifier_string(key)) { 880 type = AST_Dot; 881 } else { 882 key = make_node_from_constant(key, node); 883 } 884 } 885 return make_node(type, node, { 886 expression: value, 887 property: key, 888 }); 889 }); 890 node.value.walk(scanner); 891 }); 892 if (node.rest) { 893 fixed = false; 894 node.rest.walk(scanner); 895 } 896 fixed = save; 897 return true; 898 } 899 visit(node, fixed, function() { 900 var save_len = tw.stack.length; 901 for (var i = 0, len = scanner.stack.length - 1; i < len; i++) { 902 tw.stack.push(scanner.stack[i]); 903 } 904 node.walk(tw); 905 tw.stack.length = save_len; 906 }); 907 return true; 908 }); 909 lhs.walk(scanner); 910 } 911 912 function reduce_iife(tw, descend, compressor) { 913 var fn = this; 914 fn.inlined = false; 915 var iife = tw.parent(); 916 var sequential = !is_async(fn) && !is_generator(fn); 917 var hit = !sequential; 918 var aborts = false; 919 fn.walk(new TreeWalker(function(node) { 920 if (hit) return aborts = true; 921 if (node instanceof AST_Return) return hit = true; 922 if (node instanceof AST_Scope && node !== fn) return true; 923 })); 924 if (aborts) push(tw, sequential); 925 reset_variables(tw, compressor, fn); 926 // Virtually turn IIFE parameters into variable definitions: 927 // (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})() 928 // So existing transformation rules can work on them. 929 var safe = !fn.uses_arguments || tw.has_directive("use strict"); 930 fn.argnames.forEach(function(argname, i) { 931 var value = iife.args[i]; 932 scan_declaration(tw, compressor, argname, function() { 933 var j = fn.argnames.indexOf(argname); 934 var arg = j < 0 ? value : iife.args[j]; 935 if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0]; 936 return arg || make_node(AST_Undefined, iife); 937 }, visit); 938 }); 939 var rest = fn.rest, fixed_node; 940 if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() { 941 if (fn.rest !== rest) return rest; 942 if (!fixed_node) fixed_node = make_node(AST_Array, fn, {}); 943 fixed_node.elements = iife.args.slice(fn.argnames.length); 944 return fixed_node; 945 }, visit); 946 walk_lambda(fn, tw); 947 var safe_ids = tw.safe_ids; 948 pop_scope(tw, fn); 949 if (!aborts) tw.safe_ids = safe_ids; 950 return true; 951 952 function visit(node, fixed) { 953 var d = node.definition(); 954 if (fixed && safe && d.fixed === undefined) { 955 mark(tw, d); 956 tw.loop_ids[d.id] = tw.in_loop; 957 d.fixed = fixed; 958 d.fixed.assigns = [ node ]; 959 } else { 960 d.fixed = false; 961 } 962 } 963 } 964 965 def(AST_Assign, function(tw, descend, compressor) { 966 var node = this; 967 var left = node.left; 968 var right = node.right; 969 var ld = left instanceof AST_SymbolRef && left.definition(); 970 var scan = ld || left instanceof AST_Destructured; 971 switch (node.operator) { 972 case "=": 973 if (left.equals(right) && !left.has_side_effects(compressor)) { 974 right.walk(tw); 975 walk_prop(left); 976 node.redundant = true; 977 return true; 978 } 979 if (ld && right instanceof AST_LambdaExpression) { 980 walk_assign(); 981 right.parent_scope.resolve().fn_defs.push(right); 982 right.safe_ids = null; 983 if (!ld.fixed || !node.write_only) mark_fn_def(tw, ld, right); 984 return true; 985 } 986 if (scan) { 987 right.walk(tw); 988 walk_assign(); 989 return true; 990 } 991 mark_assignment_to_arguments(left); 992 return; 993 case "&&=": 994 case "||=": 995 case "??=": 996 var lazy = true; 997 default: 998 if (!scan) { 999 mark_assignment_to_arguments(left); 1000 return walk_lazy(); 1001 } 1002 ld.assignments++; 1003 var fixed = ld.fixed; 1004 if (is_modified(compressor, tw, node, node, 0)) { 1005 ld.fixed = false; 1006 return walk_lazy(); 1007 } 1008 var safe = safe_to_read(tw, ld); 1009 if (lazy) push(tw, true); 1010 right.walk(tw); 1011 if (lazy) pop(tw); 1012 if (safe && !left.in_arg && safe_to_assign(tw, ld)) { 1013 push_ref(ld, left); 1014 mark(tw, ld); 1015 if (ld.single_use) ld.single_use = false; 1016 left.fixed = ld.fixed = function() { 1017 return make_node(AST_Binary, node, { 1018 operator: node.operator.slice(0, -1), 1019 left: make_ref(left, fixed), 1020 right: node.right, 1021 }); 1022 }; 1023 left.fixed.assigns = !fixed || !fixed.assigns ? [ ld.orig[0] ] : fixed.assigns.slice(); 1024 left.fixed.assigns.push(node); 1025 left.fixed.to_binary = replace_ref(function(node) { 1026 return node.left; 1027 }, fixed); 1028 } else { 1029 left.walk(tw); 1030 ld.fixed = false; 1031 } 1032 return true; 1033 } 1034 1035 function walk_prop(lhs) { 1036 reset_flags(lhs); 1037 if (lhs instanceof AST_Dot) { 1038 walk_prop(lhs.expression); 1039 } else if (lhs instanceof AST_Sub) { 1040 walk_prop(lhs.expression); 1041 lhs.property.walk(tw); 1042 } else if (lhs instanceof AST_SymbolRef) { 1043 var d = lhs.definition(); 1044 push_ref(d, lhs); 1045 if (d.fixed) { 1046 lhs.fixed = d.fixed; 1047 if (lhs.fixed.assigns) { 1048 lhs.fixed.assigns.push(node); 1049 } else { 1050 lhs.fixed.assigns = [ node ]; 1051 } 1052 } 1053 } else { 1054 lhs.walk(tw); 1055 } 1056 } 1057 1058 function walk_assign() { 1059 var recursive = ld && recursive_ref(tw, ld); 1060 var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive); 1061 scan_declaration(tw, compressor, left, function() { 1062 return node.right; 1063 }, function(sym, fixed, walk) { 1064 if (!(sym instanceof AST_SymbolRef)) { 1065 mark_assignment_to_arguments(sym); 1066 walk(); 1067 return; 1068 } 1069 var d = sym.definition(); 1070 d.assignments++; 1071 if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) { 1072 walk(); 1073 d.fixed = false; 1074 } else { 1075 push_ref(d, sym); 1076 mark(tw, d); 1077 if (left instanceof AST_Destructured 1078 || d.orig.length == 1 && d.orig[0] instanceof AST_SymbolDefun) { 1079 d.single_use = false; 1080 } 1081 tw.loop_ids[d.id] = tw.in_loop; 1082 d.fixed = modified ? 0 : fixed; 1083 sym.fixed = fixed; 1084 sym.fixed.assigns = [ node ]; 1085 mark_escaped(tw, d, sym.scope, node, right, 0, 1); 1086 } 1087 }); 1088 } 1089 1090 function walk_lazy() { 1091 if (!lazy) return; 1092 left.walk(tw); 1093 push(tw, true); 1094 right.walk(tw); 1095 pop(tw); 1096 return true; 1097 } 1098 }); 1099 def(AST_Binary, function(tw) { 1100 if (!lazy_op[this.operator]) return; 1101 this.left.walk(tw); 1102 push(tw, true); 1103 this.right.walk(tw); 1104 pop(tw); 1105 return true; 1106 }); 1107 def(AST_BlockScope, function(tw, descend, compressor) { 1108 reset_block_variables(tw, compressor, this); 1109 }); 1110 def(AST_Call, function(tw, descend) { 1111 var node = this; 1112 var exp = node.expression; 1113 if (exp instanceof AST_LambdaExpression) { 1114 var iife = is_iife_single(node); 1115 node.args.forEach(function(arg) { 1116 arg.walk(tw); 1117 if (arg instanceof AST_Spread) iife = false; 1118 }); 1119 if (iife) exp.reduce_vars = reduce_iife; 1120 exp.walk(tw); 1121 if (iife) delete exp.reduce_vars; 1122 return true; 1123 } 1124 if (node.TYPE == "Call") switch (tw.in_boolean_context()) { 1125 case "d": 1126 var drop = true; 1127 case true: 1128 mark_refs(exp, drop); 1129 } 1130 exp.walk(tw); 1131 var optional = node.optional; 1132 if (optional) push(tw, true); 1133 node.args.forEach(function(arg) { 1134 arg.walk(tw); 1135 }); 1136 if (optional) pop(tw); 1137 var fixed = exp instanceof AST_SymbolRef && exp.fixed_value(); 1138 if (fixed instanceof AST_Lambda) { 1139 mark_fn_def(tw, exp.definition(), fixed); 1140 } else { 1141 tw.find_parent(AST_Scope).may_call_this(); 1142 } 1143 return true; 1144 1145 function mark_refs(node, drop) { 1146 if (node instanceof AST_Assign) { 1147 if (node.operator != "=") return; 1148 mark_refs(node.left, drop); 1149 mark_refs(node.right, drop); 1150 } else if (node instanceof AST_Binary) { 1151 if (!lazy_op[node.operator]) return; 1152 mark_refs(node.left, drop); 1153 mark_refs(node.right, drop); 1154 } else if (node instanceof AST_Conditional) { 1155 mark_refs(node.consequent, drop); 1156 mark_refs(node.alternative, drop); 1157 } else if (node instanceof AST_SymbolRef) { 1158 var def = node.definition(); 1159 def.bool_return++; 1160 if (drop) def.drop_return++; 1161 } 1162 } 1163 }); 1164 def(AST_Class, function(tw, descend, compressor) { 1165 var node = this; 1166 reset_block_variables(tw, compressor, node); 1167 if (node.extends) node.extends.walk(tw); 1168 var props = node.properties.filter(function(prop) { 1169 reset_flags(prop); 1170 if (prop.key instanceof AST_Node) { 1171 tw.push(prop); 1172 prop.key.walk(tw); 1173 tw.pop(); 1174 } 1175 return prop.value; 1176 }); 1177 if (node.name) { 1178 var d = node.name.definition(); 1179 var parent = tw.parent(); 1180 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false; 1181 if (safe_to_assign(tw, d, true)) { 1182 mark(tw, d); 1183 tw.loop_ids[d.id] = tw.in_loop; 1184 d.fixed = function() { 1185 return node; 1186 }; 1187 d.fixed.assigns = [ node ]; 1188 if (!is_safe_lexical(d)) d.single_use = false; 1189 } else { 1190 d.fixed = false; 1191 } 1192 } 1193 props.forEach(function(prop) { 1194 tw.push(prop); 1195 if (!prop.static || is_static_field_or_init(prop) && prop.value.contains_this()) { 1196 push(tw); 1197 prop.value.walk(tw); 1198 pop(tw); 1199 } else { 1200 prop.value.walk(tw); 1201 } 1202 tw.pop(); 1203 }); 1204 return true; 1205 }); 1206 def(AST_ClassInitBlock, function(tw, descend, compressor) { 1207 var node = this; 1208 push(tw, true); 1209 reset_variables(tw, compressor, node); 1210 descend(); 1211 pop_scope(tw, node); 1212 return true; 1213 }); 1214 def(AST_Conditional, function(tw) { 1215 this.condition.walk(tw); 1216 push(tw, true); 1217 this.consequent.walk(tw); 1218 pop(tw); 1219 push(tw, true); 1220 this.alternative.walk(tw); 1221 pop(tw); 1222 return true; 1223 }); 1224 def(AST_DefaultValue, function(tw) { 1225 push(tw, true); 1226 this.value.walk(tw); 1227 pop(tw); 1228 this.name.walk(tw); 1229 return true; 1230 }); 1231 def(AST_Do, function(tw) { 1232 var save_loop = tw.in_loop; 1233 tw.in_loop = this; 1234 push(tw); 1235 this.body.walk(tw); 1236 if (has_loop_control(this, tw.parent())) { 1237 pop(tw); 1238 push(tw); 1239 } 1240 this.condition.walk(tw); 1241 pop(tw); 1242 tw.in_loop = save_loop; 1243 return true; 1244 }); 1245 def(AST_For, function(tw, descend, compressor) { 1246 var node = this; 1247 reset_block_variables(tw, compressor, node); 1248 if (node.init) node.init.walk(tw); 1249 var save_loop = tw.in_loop; 1250 tw.in_loop = node; 1251 push(tw); 1252 if (node.condition) node.condition.walk(tw); 1253 node.body.walk(tw); 1254 if (node.step) { 1255 if (has_loop_control(node, tw.parent())) { 1256 pop(tw); 1257 push(tw); 1258 } 1259 node.step.walk(tw); 1260 } 1261 pop(tw); 1262 tw.in_loop = save_loop; 1263 return true; 1264 }); 1265 def(AST_ForEnumeration, function(tw, descend, compressor) { 1266 var node = this; 1267 reset_block_variables(tw, compressor, node); 1268 node.object.walk(tw); 1269 var save_loop = tw.in_loop; 1270 tw.in_loop = node; 1271 push(tw); 1272 var init = node.init; 1273 if (init instanceof AST_Definitions) { 1274 init.definitions[0].name.mark_symbol(function(node) { 1275 if (node instanceof AST_SymbolDeclaration) { 1276 var def = node.definition(); 1277 def.assignments++; 1278 def.fixed = false; 1279 } 1280 }, tw); 1281 } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) { 1282 init.mark_symbol(function(node) { 1283 if (node instanceof AST_SymbolRef) { 1284 var def = node.definition(); 1285 push_ref(def, node); 1286 def.assignments++; 1287 if (!node.is_immutable()) def.fixed = false; 1288 } 1289 }, tw); 1290 } else { 1291 init.walk(tw); 1292 } 1293 node.body.walk(tw); 1294 pop(tw); 1295 tw.in_loop = save_loop; 1296 return true; 1297 }); 1298 def(AST_If, function(tw) { 1299 this.condition.walk(tw); 1300 push(tw, true); 1301 this.body.walk(tw); 1302 pop(tw); 1303 if (this.alternative) { 1304 push(tw, true); 1305 this.alternative.walk(tw); 1306 pop(tw); 1307 } 1308 return true; 1309 }); 1310 def(AST_LabeledStatement, function(tw) { 1311 push(tw, true); 1312 this.body.walk(tw); 1313 pop(tw); 1314 return true; 1315 }); 1316 def(AST_Lambda, function(tw, descend, compressor) { 1317 var fn = this; 1318 if (!safe_to_visit(tw, fn)) return true; 1319 if (!push_uniq(tw.fn_visited, fn)) return true; 1320 fn.inlined = false; 1321 push(tw); 1322 reset_variables(tw, compressor, fn); 1323 descend(); 1324 pop_scope(tw, fn); 1325 if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1); 1326 return true; 1327 }); 1328 def(AST_LambdaDefinition, function(tw, descend, compressor) { 1329 var fn = this; 1330 var def = fn.name.definition(); 1331 var parent = tw.parent(); 1332 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false; 1333 if (!safe_to_visit(tw, fn)) return true; 1334 if (!push_uniq(tw.fn_visited, fn)) return true; 1335 fn.inlined = false; 1336 push(tw); 1337 reset_variables(tw, compressor, fn); 1338 descend(); 1339 pop_scope(tw, fn); 1340 return true; 1341 }); 1342 def(AST_Sub, function(tw) { 1343 if (!this.optional) return; 1344 this.expression.walk(tw); 1345 push(tw, true); 1346 this.property.walk(tw); 1347 pop(tw); 1348 return true; 1349 }); 1350 def(AST_Switch, function(tw, descend, compressor) { 1351 var node = this; 1352 reset_block_variables(tw, compressor, node); 1353 node.expression.walk(tw); 1354 var first = true; 1355 node.body.forEach(function(branch) { 1356 if (branch instanceof AST_Default) return; 1357 branch.expression.walk(tw); 1358 if (first) { 1359 first = false; 1360 push(tw, true); 1361 } 1362 }) 1363 if (!first) pop(tw); 1364 walk_body(node, tw); 1365 return true; 1366 }); 1367 def(AST_SwitchBranch, function(tw) { 1368 push(tw, true); 1369 walk_body(this, tw); 1370 pop(tw); 1371 return true; 1372 }); 1373 def(AST_SymbolCatch, function() { 1374 this.definition().fixed = false; 1375 }); 1376 def(AST_SymbolImport, function() { 1377 this.definition().fixed = false; 1378 }); 1379 def(AST_SymbolRef, function(tw, descend, compressor) { 1380 var ref = this; 1381 var d = ref.definition(); 1382 var fixed = d.fixed || d.last_ref && d.last_ref.fixed; 1383 push_ref(d, ref); 1384 if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) { 1385 tw.loop_ids[d.id] = tw.in_loop; 1386 } 1387 var recursive = recursive_ref(tw, d); 1388 if (recursive) recursive.enclosed.forEach(function(def) { 1389 if (d === def) return; 1390 if (def.scope.resolve() === recursive) return; 1391 var assigns = def.fixed && def.fixed.assigns; 1392 if (!assigns) return; 1393 if (assigns[assigns.length - 1] instanceof AST_VarDef) return; 1394 var safe = tw.safe_ids[def.id]; 1395 if (!safe) return; 1396 safe.assign = true; 1397 }); 1398 if (d.single_use == "m" && d.fixed) { 1399 d.fixed = 0; 1400 d.single_use = false; 1401 } 1402 switch (d.fixed) { 1403 case 0: 1404 if (!safe_to_read(tw, d)) d.fixed = false; 1405 case false: 1406 var redef = d.redefined(); 1407 if (redef && cross_scope(d.scope, ref.scope)) redef.single_use = false; 1408 break; 1409 case undefined: 1410 d.fixed = false; 1411 break; 1412 default: 1413 if (!safe_to_read(tw, d)) { 1414 d.fixed = false; 1415 break; 1416 } 1417 if (ref.in_arg && d.orig[0] instanceof AST_SymbolLambda) ref.fixed = d.scope; 1418 var value = ref.fixed_value(); 1419 if (recursive) { 1420 d.recursive_refs++; 1421 } else if (value && ref_once(compressor, d)) { 1422 d.in_loop = tw.loop_ids[d.id] !== tw.in_loop; 1423 d.single_use = is_lambda(value) 1424 && !value.pinned() 1425 && (!d.in_loop || tw.parent() instanceof AST_Call) 1426 || !d.in_loop 1427 && d.scope === ref.scope.resolve() 1428 && value.is_constant_expression(); 1429 } else { 1430 d.single_use = false; 1431 } 1432 if (is_modified(compressor, tw, ref, value, 0, is_immutable(value), recursive)) { 1433 if (d.single_use) { 1434 d.single_use = "m"; 1435 } else { 1436 d.fixed = 0; 1437 } 1438 } 1439 if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true; 1440 mark_escaped(tw, d, ref.scope, ref, value, 0, 1); 1441 break; 1442 } 1443 if (!ref.fixed) ref.fixed = d.fixed === 0 ? fixed : d.fixed; 1444 var parent; 1445 if (value instanceof AST_Lambda 1446 && !((parent = tw.parent()) instanceof AST_Call && parent.expression === ref)) { 1447 mark_fn_def(tw, d, value); 1448 } 1449 }); 1450 def(AST_Template, function(tw, descend) { 1451 var node = this; 1452 var tag = node.tag; 1453 if (!tag) return; 1454 if (tag instanceof AST_LambdaExpression) { 1455 node.expressions.forEach(function(exp) { 1456 exp.walk(tw); 1457 }); 1458 tag.walk(tw); 1459 return true; 1460 } 1461 tag.walk(tw); 1462 node.expressions.forEach(function(exp) { 1463 exp.walk(tw); 1464 }); 1465 var fixed = tag instanceof AST_SymbolRef && tag.fixed_value(); 1466 if (fixed instanceof AST_Lambda) { 1467 mark_fn_def(tw, tag.definition(), fixed); 1468 } else { 1469 tw.find_parent(AST_Scope).may_call_this(); 1470 } 1471 return true; 1472 }); 1473 def(AST_Toplevel, function(tw, descend, compressor) { 1474 var node = this; 1475 node.globals.each(function(def) { 1476 reset_def(tw, compressor, def); 1477 }); 1478 push(tw, true); 1479 reset_variables(tw, compressor, node); 1480 descend(); 1481 pop_scope(tw, node); 1482 return true; 1483 }); 1484 def(AST_Try, function(tw, descend, compressor) { 1485 var node = this; 1486 reset_block_variables(tw, compressor, node); 1487 push(tw, true); 1488 walk_body(node, tw); 1489 pop(tw); 1490 if (node.bcatch) { 1491 push(tw, true); 1492 node.bcatch.walk(tw); 1493 pop(tw); 1494 } 1495 if (node.bfinally) node.bfinally.walk(tw); 1496 return true; 1497 }); 1498 def(AST_Unary, function(tw, descend) { 1499 var node = this; 1500 if (!UNARY_POSTFIX[node.operator]) return; 1501 var exp = node.expression; 1502 if (!(exp instanceof AST_SymbolRef)) { 1503 mark_assignment_to_arguments(exp); 1504 return; 1505 } 1506 var d = exp.definition(); 1507 d.assignments++; 1508 var fixed = d.fixed; 1509 if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) { 1510 push_ref(d, exp); 1511 mark(tw, d); 1512 if (d.single_use) d.single_use = false; 1513 d.fixed = function() { 1514 return make_node(AST_Binary, node, { 1515 operator: node.operator.slice(0, -1), 1516 left: make_node(AST_UnaryPrefix, node, { 1517 operator: "+", 1518 expression: make_ref(exp, fixed), 1519 }), 1520 right: make_node(AST_Number, node, { value: 1 }), 1521 }); 1522 }; 1523 d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : []; 1524 d.fixed.assigns.push(node); 1525 if (node instanceof AST_UnaryPrefix) { 1526 exp.fixed = d.fixed; 1527 } else { 1528 exp.fixed = function() { 1529 return make_node(AST_UnaryPrefix, node, { 1530 operator: "+", 1531 expression: make_ref(exp, fixed), 1532 }); 1533 }; 1534 exp.fixed.assigns = fixed && fixed.assigns; 1535 exp.fixed.to_prefix = replace_ref(function(node) { 1536 return node.expression; 1537 }, d.fixed); 1538 } 1539 } else { 1540 exp.walk(tw); 1541 d.fixed = false; 1542 } 1543 return true; 1544 }); 1545 def(AST_VarDef, function(tw, descend, compressor) { 1546 var node = this; 1547 var value = node.value; 1548 if (value instanceof AST_LambdaExpression && node.name instanceof AST_SymbolDeclaration) { 1549 walk_defn(); 1550 value.parent_scope.resolve().fn_defs.push(value); 1551 value.safe_ids = null; 1552 var ld = node.name.definition(); 1553 if (!ld.fixed) mark_fn_def(tw, ld, value); 1554 } else if (value) { 1555 value.walk(tw); 1556 walk_defn(); 1557 } else if (tw.parent() instanceof AST_Let) { 1558 walk_defn(); 1559 } 1560 return true; 1561 1562 function walk_defn() { 1563 scan_declaration(tw, compressor, node.name, function() { 1564 return node.value || make_node(AST_Undefined, node); 1565 }, function(name, fixed) { 1566 var d = name.definition(); 1567 if (fixed && safe_to_assign(tw, d, true)) { 1568 mark(tw, d); 1569 tw.loop_ids[d.id] = tw.in_loop; 1570 d.fixed = fixed; 1571 d.fixed.assigns = [ node ]; 1572 if (name instanceof AST_SymbolConst && d.redefined() 1573 || !(can_drop_symbol(name) || is_safe_lexical(d))) { 1574 d.single_use = false; 1575 } 1576 } else { 1577 d.fixed = false; 1578 } 1579 }); 1580 } 1581 }); 1582 def(AST_While, function(tw, descend) { 1583 var save_loop = tw.in_loop; 1584 tw.in_loop = this; 1585 push(tw); 1586 descend(); 1587 pop(tw); 1588 tw.in_loop = save_loop; 1589 return true; 1590 }); 1591 })(function(node, func) { 1592 node.DEFMETHOD("reduce_vars", func); 1593 }); 1594 1595 function reset_flags(node) { 1596 node._squeezed = false; 1597 node._optimized = false; 1598 if (node instanceof AST_BlockScope) node._var_names = undefined; 1599 if (node instanceof AST_SymbolRef) node.fixed = undefined; 1600 } 1601 1602 AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) { 1603 var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) { 1604 reset_flags(node); 1605 return node.reduce_vars(tw, descend, compressor); 1606 } : reset_flags); 1607 // Flow control for visiting lambda definitions 1608 tw.fn_scanning = null; 1609 tw.fn_visited = []; 1610 // Record the loop body in which `AST_SymbolDeclaration` is first encountered 1611 tw.in_loop = null; 1612 tw.loop_ids = Object.create(null); 1613 // Stack of look-up tables to keep track of whether a `SymbolDef` has been 1614 // properly assigned before use: 1615 // - `push()` & `pop()` when visiting conditional branches 1616 // - backup & restore via `save_ids` when visiting out-of-order sections 1617 tw.safe_ids = Object.create(null); 1618 tw.safe_ids.seq = {}; 1619 this.walk(tw); 1620 }); 1621 1622 AST_Symbol.DEFMETHOD("fixed_value", function(ref_only) { 1623 var def = this.definition(); 1624 var fixed = def.fixed; 1625 if (fixed) { 1626 if (this.fixed) fixed = this.fixed; 1627 return (fixed instanceof AST_Node ? fixed : fixed()).tail_node(); 1628 } 1629 fixed = fixed === 0 && this.fixed; 1630 if (!fixed) return fixed; 1631 var value = (fixed instanceof AST_Node ? fixed : fixed()).tail_node(); 1632 if (ref_only && def.escaped.depth != 1 && is_object(value, true)) return value; 1633 if (value.is_constant()) return value; 1634 }); 1635 1636 AST_SymbolRef.DEFMETHOD("is_immutable", function() { 1637 var def = this.redef || this.definition(); 1638 if (!(def.orig[0] instanceof AST_SymbolLambda)) return false; 1639 if (def.orig.length == 1) return true; 1640 if (!this.in_arg) return false; 1641 return !(def.orig[1] instanceof AST_SymbolFunarg); 1642 }); 1643 1644 AST_Node.DEFMETHOD("convert_symbol", noop); 1645 function convert_destructured(type, process) { 1646 return this.transform(new TreeTransformer(function(node, descend) { 1647 if (node instanceof AST_DefaultValue) { 1648 node = node.clone(); 1649 node.name = node.name.transform(this); 1650 return node; 1651 } 1652 if (node instanceof AST_Destructured) { 1653 node = node.clone(); 1654 descend(node, this); 1655 return node; 1656 } 1657 if (node instanceof AST_DestructuredKeyVal) { 1658 node = node.clone(); 1659 node.value = node.value.transform(this); 1660 return node; 1661 } 1662 return node.convert_symbol(type, process); 1663 })); 1664 } 1665 AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured); 1666 AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured); 1667 function convert_symbol(type, process) { 1668 var node = make_node(type, this); 1669 return process(node, this) || node; 1670 } 1671 AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol); 1672 AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol); 1673 1674 function process_to_assign(ref) { 1675 var def = ref.definition(); 1676 def.assignments++; 1677 def.references.push(ref); 1678 } 1679 1680 function mark_destructured(process, tw) { 1681 var marker = new TreeWalker(function(node) { 1682 if (node instanceof AST_DefaultValue) { 1683 node.value.walk(tw); 1684 node.name.walk(marker); 1685 return true; 1686 } 1687 if (node instanceof AST_DestructuredKeyVal) { 1688 if (node.key instanceof AST_Node) node.key.walk(tw); 1689 node.value.walk(marker); 1690 return true; 1691 } 1692 return process(node); 1693 }); 1694 this.walk(marker); 1695 } 1696 AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured); 1697 AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured); 1698 function mark_symbol(process) { 1699 return process(this); 1700 } 1701 AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol); 1702 AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol); 1703 1704 AST_Node.DEFMETHOD("match_symbol", function(predicate) { 1705 return predicate(this); 1706 }); 1707 function match_destructured(predicate, ignore_side_effects) { 1708 var found = false; 1709 var tw = new TreeWalker(function(node) { 1710 if (found) return true; 1711 if (node instanceof AST_DefaultValue) { 1712 if (!ignore_side_effects) return found = true; 1713 node.name.walk(tw); 1714 return true; 1715 } 1716 if (node instanceof AST_DestructuredKeyVal) { 1717 if (!ignore_side_effects && node.key instanceof AST_Node) return found = true; 1718 node.value.walk(tw); 1719 return true; 1720 } 1721 if (predicate(node)) return found = true; 1722 }); 1723 this.walk(tw); 1724 return found; 1725 } 1726 AST_DefaultValue.DEFMETHOD("match_symbol", match_destructured); 1727 AST_Destructured.DEFMETHOD("match_symbol", match_destructured); 1728 1729 function in_async_generator(scope) { 1730 return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction; 1731 } 1732 1733 function find_scope(compressor) { 1734 var level = 0, node = compressor.self(); 1735 do { 1736 if (node.variables) return node; 1737 } while (node = compressor.parent(level++)); 1738 } 1739 1740 function find_try(compressor, level, node, scope, may_throw, sync) { 1741 for (var parent; parent = compressor.parent(level++); node = parent) { 1742 if (parent === scope) return false; 1743 if (sync && parent instanceof AST_Lambda) { 1744 if (parent.name || is_async(parent) || is_generator(parent)) return true; 1745 } else if (parent instanceof AST_Try) { 1746 if (parent.bfinally && parent.bfinally !== node) return true; 1747 if (may_throw && parent.bcatch && parent.bcatch !== node) return true; 1748 } 1749 } 1750 return false; 1751 } 1752 1753 var identifier_atom = makePredicate("Infinity NaN undefined"); 1754 function is_lhs_read_only(lhs, compressor) { 1755 if (lhs instanceof AST_Atom) return true; 1756 if (lhs instanceof AST_ObjectIdentity) return true; 1757 if (lhs instanceof AST_PropAccess) { 1758 if (lhs.property === "__proto__") return true; 1759 lhs = lhs.expression; 1760 if (lhs instanceof AST_SymbolRef) { 1761 if (lhs.is_immutable()) return false; 1762 lhs = lhs.fixed_value(); 1763 } 1764 if (!lhs) return true; 1765 if (lhs.tail_node().is_constant()) return true; 1766 return is_lhs_read_only(lhs, compressor); 1767 } 1768 if (lhs instanceof AST_SymbolRef) { 1769 if (lhs.is_immutable()) return true; 1770 var def = lhs.definition(); 1771 return compressor.exposed(def) && identifier_atom[def.name]; 1772 } 1773 return false; 1774 } 1775 1776 function make_node(ctor, orig, props) { 1777 if (props) { 1778 props.start = orig.start; 1779 props.end = orig.end; 1780 } else { 1781 props = orig; 1782 } 1783 return new ctor(props); 1784 } 1785 1786 function make_sequence(orig, expressions) { 1787 if (expressions.length == 1) return expressions[0]; 1788 return make_node(AST_Sequence, orig, { expressions: expressions.reduce(merge_sequence, []) }); 1789 } 1790 1791 function make_node_from_constant(val, orig) { 1792 switch (typeof val) { 1793 case "string": 1794 return make_node(AST_String, orig, { value: val }); 1795 case "number": 1796 if (isNaN(val)) return make_node(AST_NaN, orig); 1797 if (isFinite(val)) { 1798 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, { 1799 operator: "-", 1800 expression: make_node(AST_Number, orig, { value: -val }), 1801 }) : make_node(AST_Number, orig, { value: val }); 1802 } 1803 return val < 0 ? make_node(AST_UnaryPrefix, orig, { 1804 operator: "-", 1805 expression: make_node(AST_Infinity, orig), 1806 }) : make_node(AST_Infinity, orig); 1807 case "boolean": 1808 return make_node(val ? AST_True : AST_False, orig); 1809 case "undefined": 1810 return make_node(AST_Undefined, orig); 1811 default: 1812 if (val === null) { 1813 return make_node(AST_Null, orig); 1814 } 1815 if (val instanceof RegExp) { 1816 return make_node(AST_RegExp, orig, { value: val }); 1817 } 1818 throw new Error(string_template("Can't handle constant of type: {type}", { type: typeof val })); 1819 } 1820 } 1821 1822 function needs_unbinding(val) { 1823 return val instanceof AST_PropAccess 1824 || is_undeclared_ref(val) && val.name == "eval"; 1825 } 1826 1827 // we shouldn't compress (1,func)(something) to 1828 // func(something) because that changes the meaning of 1829 // the func (becomes lexical instead of global). 1830 function maintain_this_binding(parent, orig, val) { 1831 var wrap = false; 1832 if (parent.TYPE == "Call") { 1833 wrap = parent.expression === orig && needs_unbinding(val); 1834 } else if (parent instanceof AST_Template) { 1835 wrap = parent.tag === orig && needs_unbinding(val); 1836 } else if (parent instanceof AST_UnaryPrefix) { 1837 wrap = parent.operator == "delete" 1838 || parent.operator == "typeof" && is_undeclared_ref(val); 1839 } 1840 return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val; 1841 } 1842 1843 function merge_expression(base, target) { 1844 var fixed_by_id = new Dictionary(); 1845 base.walk(new TreeWalker(function(node) { 1846 if (!(node instanceof AST_SymbolRef)) return; 1847 var def = node.definition(); 1848 var fixed = node.fixed; 1849 if (!fixed || !fixed_by_id.has(def.id)) { 1850 fixed_by_id.set(def.id, fixed); 1851 } else if (fixed_by_id.get(def.id) !== fixed) { 1852 fixed_by_id.set(def.id, false); 1853 } 1854 })); 1855 if (fixed_by_id.size() > 0) target.walk(new TreeWalker(function(node) { 1856 if (!(node instanceof AST_SymbolRef)) return; 1857 var def = node.definition(); 1858 var fixed = node.fixed; 1859 if (!fixed || !fixed_by_id.has(def.id)) return; 1860 if (fixed_by_id.get(def.id) !== fixed) node.fixed = false; 1861 })); 1862 return target; 1863 } 1864 1865 function merge_sequence(array, node) { 1866 if (node instanceof AST_Sequence) { 1867 [].push.apply(array, node.expressions); 1868 } else { 1869 array.push(node); 1870 } 1871 return array; 1872 } 1873 1874 function is_lexical_definition(stat) { 1875 return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let; 1876 } 1877 1878 function safe_to_trim(stat) { 1879 if (stat instanceof AST_LambdaDefinition) { 1880 var def = stat.name.definition(); 1881 var scope = stat.name.scope; 1882 return def.scope === scope || all(def.references, function(ref) { 1883 var s = ref.scope; 1884 do { 1885 if (s === scope) return true; 1886 } while (s = s.parent_scope); 1887 }); 1888 } 1889 return !is_lexical_definition(stat); 1890 } 1891 1892 function as_statement_array(thing) { 1893 if (thing === null) return []; 1894 if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ]; 1895 if (thing instanceof AST_EmptyStatement) return []; 1896 if (is_statement(thing)) return [ thing ]; 1897 throw new Error("Can't convert thing to statement array"); 1898 } 1899 1900 function is_empty(thing) { 1901 if (thing === null) return true; 1902 if (thing instanceof AST_EmptyStatement) return true; 1903 if (thing instanceof AST_BlockStatement) return thing.body.length == 0; 1904 return false; 1905 } 1906 1907 function has_declarations_only(block) { 1908 return all(block.body, function(stat) { 1909 return is_empty(stat) 1910 || stat instanceof AST_Defun 1911 || stat instanceof AST_Var && declarations_only(stat); 1912 }); 1913 } 1914 1915 function loop_body(x) { 1916 if (x instanceof AST_IterationStatement) { 1917 return x.body instanceof AST_BlockStatement ? x.body : x; 1918 } 1919 return x; 1920 } 1921 1922 function is_iife_call(node) { 1923 if (node.TYPE != "Call") return false; 1924 do { 1925 node = node.expression; 1926 } while (node instanceof AST_PropAccess); 1927 return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node); 1928 } 1929 1930 function is_iife_single(call) { 1931 var exp = call.expression; 1932 if (exp.name) return false; 1933 if (!(call instanceof AST_New)) return true; 1934 var found = false; 1935 exp.walk(new TreeWalker(function(node) { 1936 if (found) return true; 1937 if (node instanceof AST_NewTarget) return found = true; 1938 if (node instanceof AST_Scope && node !== exp) return true; 1939 })); 1940 return !found; 1941 } 1942 1943 function is_undeclared_ref(node) { 1944 return node instanceof AST_SymbolRef && node.definition().undeclared; 1945 } 1946 1947 var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Map Math Number parseFloat parseInt RangeError ReferenceError RegExp Object Set setInterval setTimeout String SyntaxError TypeError unescape URIError WeakMap WeakSet"); 1948 AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) { 1949 return this.defined 1950 || !this.definition().undeclared 1951 || compressor.option("unsafe") && global_names[this.name]; 1952 }); 1953 1954 function is_static_field_or_init(prop) { 1955 return prop.static && prop.value && (prop instanceof AST_ClassField || prop instanceof AST_ClassInit); 1956 } 1957 1958 function declarations_only(node) { 1959 return all(node.definitions, function(var_def) { 1960 return !var_def.value; 1961 }); 1962 } 1963 1964 function is_declaration(stat, lexical) { 1965 if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) { 1966 if (prop.key instanceof AST_Node) return false; 1967 return !is_static_field_or_init(prop); 1968 }); 1969 if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat); 1970 if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical); 1971 if (stat instanceof AST_ExportDefault) return is_declaration(stat.body, lexical); 1972 return stat instanceof AST_LambdaDefinition; 1973 } 1974 1975 function is_last_statement(body, stat) { 1976 var index = body.lastIndexOf(stat); 1977 if (index < 0) return false; 1978 while (++index < body.length) { 1979 if (!is_declaration(body[index], true)) return false; 1980 } 1981 return true; 1982 } 1983 1984 // Certain combination of unused name + side effect leads to invalid AST: 1985 // https://github.com/mishoo/UglifyJS/issues/44 1986 // https://github.com/mishoo/UglifyJS/issues/1838 1987 // https://github.com/mishoo/UglifyJS/issues/3371 1988 // We fix it at this stage by moving the `var` outside the `for`. 1989 function patch_for_init(node, in_list) { 1990 var block; 1991 if (node.init instanceof AST_BlockStatement) { 1992 block = node.init; 1993 node.init = block.body.pop(); 1994 block.body.push(node); 1995 } 1996 if (node.init instanceof AST_Defun) { 1997 if (!block) block = make_node(AST_BlockStatement, node, { body: [ node ] }); 1998 block.body.splice(-1, 0, node.init); 1999 node.init = null; 2000 } else if (node.init instanceof AST_SimpleStatement) { 2001 node.init = node.init.body; 2002 } else if (is_empty(node.init)) { 2003 node.init = null; 2004 } 2005 if (!block) return; 2006 return in_list ? List.splice(block.body) : block; 2007 } 2008 2009 function tighten_body(statements, compressor) { 2010 var in_lambda = last_of(compressor, function(node) { 2011 return node instanceof AST_Lambda; 2012 }); 2013 var block_scope, iife_in_try, in_iife_single, in_loop, in_try, scope; 2014 find_loop_scope_try(); 2015 var changed, last_changed, max_iter = 10; 2016 do { 2017 last_changed = changed; 2018 changed = 0; 2019 if (eliminate_spurious_blocks(statements)) changed = 1; 2020 if (!changed && last_changed == 1) break; 2021 if (compressor.option("dead_code")) { 2022 if (eliminate_dead_code(statements, compressor)) changed = 2; 2023 if (!changed && last_changed == 2) break; 2024 } 2025 if (compressor.option("if_return")) { 2026 if (handle_if_return(statements, compressor)) changed = 3; 2027 if (!changed && last_changed == 3) break; 2028 } 2029 if (compressor.option("awaits") && compressor.option("side_effects")) { 2030 if (trim_awaits(statements, compressor)) changed = 4; 2031 if (!changed && last_changed == 4) break; 2032 } 2033 if (compressor.option("inline") >= 4) { 2034 if (inline_iife(statements, compressor)) changed = 5; 2035 if (!changed && last_changed == 5) break; 2036 } 2037 if (compressor.sequences_limit > 0) { 2038 if (sequencesize(statements, compressor)) changed = 6; 2039 if (!changed && last_changed == 6) break; 2040 if (sequencesize_2(statements, compressor)) changed = 7; 2041 if (!changed && last_changed == 7) break; 2042 } 2043 if (compressor.option("join_vars")) { 2044 if (join_consecutive_vars(statements)) changed = 8; 2045 if (!changed && last_changed == 8) break; 2046 } 2047 if (compressor.option("collapse_vars")) { 2048 if (collapse(statements, compressor)) changed = 9; 2049 } 2050 } while (changed && max_iter-- > 0); 2051 return statements; 2052 2053 function last_of(compressor, predicate) { 2054 var block = compressor.self(), level = 0, stat; 2055 do { 2056 if (block instanceof AST_Catch) { 2057 block = compressor.parent(level++); 2058 } else if (block instanceof AST_LabeledStatement) { 2059 block = block.body; 2060 } else if (block instanceof AST_SwitchBranch) { 2061 var branches = compressor.parent(level); 2062 if (branches.body[branches.body.length - 1] === block || has_break(block.body)) { 2063 level++; 2064 block = branches; 2065 } 2066 } 2067 do { 2068 stat = block; 2069 if (predicate(stat)) return stat; 2070 block = compressor.parent(level++); 2071 } while (block instanceof AST_If); 2072 } while (stat 2073 && (block instanceof AST_BlockStatement 2074 || block instanceof AST_Catch 2075 || block instanceof AST_Scope 2076 || block instanceof AST_SwitchBranch 2077 || block instanceof AST_Try) 2078 && is_last_statement(block.body, stat)); 2079 2080 function has_break(stats) { 2081 for (var i = stats.length; --i >= 0;) { 2082 if (stats[i] instanceof AST_Break) return true; 2083 } 2084 return false; 2085 } 2086 } 2087 2088 function find_loop_scope_try() { 2089 var node = compressor.self(), level = 0; 2090 do { 2091 if (!block_scope && node.variables) block_scope = node; 2092 if (node instanceof AST_Catch) { 2093 if (compressor.parent(level).bfinally) { 2094 if (!in_try) in_try = {}; 2095 in_try.bfinally = true; 2096 } 2097 level++; 2098 } else if (node instanceof AST_Finally) { 2099 level++; 2100 } else if (node instanceof AST_IterationStatement) { 2101 in_loop = true; 2102 } else if (node instanceof AST_Scope) { 2103 scope = node; 2104 break; 2105 } else if (node instanceof AST_Try) { 2106 if (!in_try) in_try = {}; 2107 if (node.bcatch) in_try.bcatch = true; 2108 if (node.bfinally) in_try.bfinally = true; 2109 } 2110 } while (node = compressor.parent(level++)); 2111 } 2112 2113 // Search from right to left for assignment-like expressions: 2114 // - `var a = x;` 2115 // - `a = x;` 2116 // - `++a` 2117 // For each candidate, scan from left to right for first usage, then try 2118 // to fold assignment into the site for compression. 2119 // Will not attempt to collapse assignments into or past code blocks 2120 // which are not sequentially executed, e.g. loops and conditionals. 2121 function collapse(statements, compressor) { 2122 if (scope.pinned()) return; 2123 var args; 2124 var assignments = new Dictionary(); 2125 var candidates = []; 2126 var changed = false; 2127 var declare_only = new Dictionary(); 2128 var force_single; 2129 var stat_index = statements.length; 2130 var scanner = new TreeTransformer(function(node, descend) { 2131 if (abort) return node; 2132 // Skip nodes before `candidate` as quickly as possible 2133 if (!hit) { 2134 if (node !== hit_stack[hit_index]) return node; 2135 hit_index++; 2136 if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner); 2137 hit = true; 2138 stop_after = (value_def ? find_stop_value : find_stop)(node, 0); 2139 if (stop_after === node) abort = true; 2140 return node; 2141 } 2142 var parent = scanner.parent(); 2143 // Stop only if candidate is found within conditional branches 2144 if (!stop_if_hit && in_conditional(node, parent)) { 2145 stop_if_hit = parent; 2146 } 2147 // Cascade compound assignments 2148 if (compound && scan_lhs && can_replace && !stop_if_hit 2149 && node instanceof AST_Assign && node.operator != "=" && node.left.equals(lhs)) { 2150 replaced++; 2151 changed = true; 2152 AST_Node.info("Cascading {this} [{start}]", node); 2153 can_replace = false; 2154 lvalues = get_lvalues(lhs); 2155 node.right.transform(scanner); 2156 clear_write_only(candidate); 2157 var folded; 2158 if (abort) { 2159 folded = candidate; 2160 } else { 2161 abort = true; 2162 folded = make_node(AST_Binary, candidate, { 2163 operator: compound, 2164 left: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_binary(candidate) : lhs, 2165 right: rvalue, 2166 }); 2167 } 2168 return make_node(AST_Assign, node, { 2169 operator: "=", 2170 left: node.left, 2171 right: make_node(AST_Binary, node, { 2172 operator: node.operator.slice(0, -1), 2173 left: folded, 2174 right: node.right, 2175 }), 2176 }); 2177 } 2178 // Stop immediately if these node types are encountered 2179 if (should_stop(node, parent)) { 2180 abort = true; 2181 return node; 2182 } 2183 // Skip transient nodes caused by single-use variable replacement 2184 if (node.single_use) return node; 2185 // Replace variable with assignment when found 2186 var hit_rhs; 2187 if (!(node instanceof AST_SymbolDeclaration) 2188 && (scan_lhs && lhs.equals(node) 2189 || scan_rhs && (hit_rhs = scan_rhs(node, this)))) { 2190 if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) { 2191 if (!hit_rhs && !value_def) abort = true; 2192 return node; 2193 } 2194 if (is_lhs(node, parent)) { 2195 if (value_def && !hit_rhs) assign_used = true; 2196 return node; 2197 } 2198 if (!hit_rhs && verify_ref && node.fixed !== lhs.fixed) { 2199 abort = true; 2200 return node; 2201 } 2202 if (value_def) { 2203 if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced; 2204 if (!hit_rhs) replaced++; 2205 return node; 2206 } 2207 replaced++; 2208 changed = abort = true; 2209 AST_Node.info("Collapsing {this} [{start}]", node); 2210 if (candidate.TYPE == "Binary") { 2211 update_symbols(candidate, node); 2212 return make_node(AST_Assign, candidate, { 2213 operator: "=", 2214 left: candidate.right.left, 2215 right: candidate.operator == "&&" ? make_node(AST_Conditional, candidate, { 2216 condition: candidate.left, 2217 consequent: candidate.right.right, 2218 alternative: node, 2219 }) : make_node(AST_Conditional, candidate, { 2220 condition: candidate.left, 2221 consequent: node, 2222 alternative: candidate.right.right, 2223 }), 2224 }); 2225 } 2226 if (candidate instanceof AST_UnaryPostfix) return make_node(AST_UnaryPrefix, candidate, { 2227 operator: candidate.operator, 2228 expression: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_prefix(candidate) : lhs, 2229 }); 2230 if (candidate instanceof AST_UnaryPrefix) { 2231 clear_write_only(candidate); 2232 return candidate; 2233 } 2234 update_symbols(rvalue, node); 2235 if (candidate instanceof AST_VarDef) { 2236 var def = candidate.name.definition(); 2237 if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { 2238 def.replaced++; 2239 return maintain_this_binding(parent, node, rvalue); 2240 } 2241 return make_node(AST_Assign, candidate, { 2242 operator: "=", 2243 left: node, 2244 right: rvalue, 2245 }); 2246 } 2247 clear_write_only(rvalue); 2248 var assign = candidate.clone(); 2249 assign.right = rvalue; 2250 return assign; 2251 } 2252 // Stop signals related to AST_SymbolRef 2253 if (should_stop_ref(node, parent)) { 2254 abort = true; 2255 return node; 2256 } 2257 // These node types have child nodes that execute sequentially, 2258 // but are otherwise not safe to scan into or beyond them. 2259 if (is_last_node(node, parent) || may_throw(node)) { 2260 stop_after = node; 2261 if (node instanceof AST_Scope) abort = true; 2262 } 2263 // Scan but don't replace inside getter/setter 2264 if (node instanceof AST_Accessor) { 2265 var replace = can_replace; 2266 can_replace = false; 2267 descend(node, scanner); 2268 can_replace = replace; 2269 return signal_abort(node); 2270 } 2271 // Scan but don't replace inside destructuring expression 2272 if (node instanceof AST_Destructured) { 2273 var replace = can_replace; 2274 can_replace = false; 2275 descend(node, scanner); 2276 can_replace = replace; 2277 return signal_abort(node); 2278 } 2279 // Scan but don't replace inside default value 2280 if (node instanceof AST_DefaultValue) { 2281 node.name = node.name.transform(scanner); 2282 var replace = can_replace; 2283 can_replace = false; 2284 node.value = node.value.transform(scanner); 2285 can_replace = replace; 2286 return signal_abort(node); 2287 } 2288 // Scan but don't replace inside block scope with colliding variable 2289 if (node instanceof AST_BlockScope 2290 && !(node instanceof AST_Scope) 2291 && !(node.variables && node.variables.all(function(def) { 2292 return !enclosed.has(def.name) && !lvalues.has(def.name); 2293 }))) { 2294 var replace = can_replace; 2295 can_replace = false; 2296 if (!handle_custom_scan_order(node, scanner)) descend(node, scanner); 2297 can_replace = replace; 2298 return signal_abort(node); 2299 } 2300 if (handle_custom_scan_order(node, scanner)) return signal_abort(node); 2301 }, signal_abort); 2302 var multi_replacer = new TreeTransformer(function(node) { 2303 if (abort) return node; 2304 // Skip nodes before `candidate` as quickly as possible 2305 if (!hit) { 2306 if (node !== hit_stack[hit_index]) return node; 2307 hit_index++; 2308 switch (hit_stack.length - hit_index) { 2309 case 0: 2310 hit = true; 2311 if (assign_used) return node; 2312 if (node !== candidate) return node; 2313 if (node instanceof AST_VarDef) return node; 2314 def.replaced++; 2315 var parent = multi_replacer.parent(); 2316 if (parent instanceof AST_Sequence && parent.tail_node() !== node) { 2317 value_def.replaced++; 2318 if (rvalue === rhs_value) return List.skip; 2319 return make_sequence(rhs_value, rhs_value.expressions.slice(0, -1)); 2320 } 2321 return rvalue; 2322 case 1: 2323 if (!assign_used && node.body === candidate) { 2324 hit = true; 2325 def.replaced++; 2326 value_def.replaced++; 2327 return null; 2328 } 2329 default: 2330 return handle_custom_scan_order(node, multi_replacer); 2331 } 2332 } 2333 // Replace variable when found 2334 if (node instanceof AST_SymbolRef && node.definition() === def) { 2335 if (is_lhs(node, multi_replacer.parent())) return node; 2336 if (!--replaced) abort = true; 2337 AST_Node.info("Replacing {this} [{start}]", node); 2338 var ref = rvalue.clone(); 2339 ref.scope = node.scope; 2340 ref.reference(); 2341 if (replaced == assign_pos) { 2342 abort = true; 2343 return make_node(AST_Assign, candidate, { 2344 operator: "=", 2345 left: node, 2346 right: ref, 2347 }); 2348 } 2349 def.replaced++; 2350 return ref; 2351 } 2352 // Skip (non-executed) functions and (leading) default case in switch statements 2353 if (node instanceof AST_Default || node instanceof AST_Scope) return node; 2354 }, function(node) { 2355 return patch_sequence(node, multi_replacer); 2356 }); 2357 while (--stat_index >= 0) { 2358 // Treat parameters as collapsible in IIFE, i.e. 2359 // function(a, b){ ... }(x()); 2360 // would be translated into equivalent assignments: 2361 // var a = x(), b = undefined; 2362 if (stat_index == 0 && compressor.option("unused")) extract_args(); 2363 // Find collapsible assignments 2364 var hit_stack = []; 2365 extract_candidates(statements[stat_index]); 2366 while (candidates.length > 0) { 2367 hit_stack = candidates.pop(); 2368 var hit_index = 0; 2369 var candidate = hit_stack[hit_stack.length - 1]; 2370 var assign_pos = -1; 2371 var assign_used = false; 2372 var verify_ref = false; 2373 var remaining; 2374 var value_def = null; 2375 var stop_after = null; 2376 var stop_if_hit = null; 2377 var lhs = get_lhs(candidate); 2378 var side_effects = lhs && lhs.has_side_effects(compressor); 2379 var scan_lhs = lhs && (!side_effects || lhs instanceof AST_SymbolRef) 2380 && !is_lhs_read_only(lhs, compressor); 2381 var scan_rhs = foldable(candidate); 2382 if (!scan_lhs && !scan_rhs) continue; 2383 var compound = candidate instanceof AST_Assign && candidate.operator.slice(0, -1); 2384 var funarg = candidate.name instanceof AST_SymbolFunarg; 2385 var may_throw = return_false; 2386 if (candidate.may_throw(compressor)) { 2387 if (funarg && is_async(scope)) continue; 2388 may_throw = in_try ? function(node) { 2389 return node.has_side_effects(compressor); 2390 } : side_effects_external; 2391 } 2392 var read_toplevel = false; 2393 var modify_toplevel = false; 2394 // Locate symbols which may execute code outside of scanning range 2395 var enclosed = new Dictionary(); 2396 var well_defined = true; 2397 var lvalues = get_lvalues(candidate); 2398 var lhs_local = is_lhs_local(lhs); 2399 var rhs_value = get_rvalue(candidate); 2400 var rvalue = rhs_value; 2401 if (!side_effects) { 2402 if (!compound && rvalue instanceof AST_Sequence) rvalue = rvalue.tail_node(); 2403 side_effects = value_has_side_effects(); 2404 } 2405 var check_destructured = in_try || !lhs_local ? function(node) { 2406 return node instanceof AST_Destructured; 2407 } : return_false; 2408 var replace_all = replace_all_symbols(candidate); 2409 var hit = funarg; 2410 var abort = false; 2411 var replaced = 0; 2412 var can_replace = !args || !hit; 2413 if (!can_replace) { 2414 for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) { 2415 if (args[j]) args[j].transform(scanner); 2416 } 2417 can_replace = true; 2418 } 2419 for (var i = stat_index; !abort && i < statements.length; i++) { 2420 statements[i].transform(scanner); 2421 } 2422 if (value_def) { 2423 if (!replaced || remaining > replaced + assign_used) { 2424 candidates.push(hit_stack); 2425 force_single = true; 2426 continue; 2427 } 2428 if (replaced == assign_pos) assign_used = true; 2429 var def = lhs.definition(); 2430 abort = false; 2431 hit_index = 0; 2432 hit = funarg; 2433 for (var i = stat_index; !abort && i < statements.length; i++) { 2434 if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1); 2435 } 2436 replaced = candidate instanceof AST_VarDef 2437 && candidate === hit_stack[hit_stack.length - 1] 2438 && def.references.length == def.replaced 2439 && !compressor.exposed(def); 2440 value_def.last_ref = false; 2441 value_def.single_use = false; 2442 changed = true; 2443 } 2444 if (replaced) remove_candidate(candidate); 2445 } 2446 } 2447 return changed; 2448 2449 function signal_abort(node) { 2450 if (abort) return node; 2451 if (stop_after === node) abort = true; 2452 if (stop_if_hit === node) stop_if_hit = null; 2453 return node; 2454 } 2455 2456 function handle_custom_scan_order(node, tt) { 2457 if (!(node instanceof AST_BlockScope)) return; 2458 // Skip (non-executed) functions 2459 if (node instanceof AST_Scope) return node; 2460 // Scan computed keys, static fields & initializers in class 2461 if (node instanceof AST_Class) { 2462 if (node.name) node.name = node.name.transform(tt); 2463 if (!abort && node.extends) node.extends = node.extends.transform(tt); 2464 var fields = [], stats = []; 2465 for (var i = 0; !abort && i < node.properties.length; i++) { 2466 var prop = node.properties[i]; 2467 if (prop.key instanceof AST_Node) prop.key = prop.key.transform(tt); 2468 if (!prop.static) continue; 2469 if (prop instanceof AST_ClassField) { 2470 if (prop.value) fields.push(prop); 2471 } else if (prop instanceof AST_ClassInit) { 2472 [].push.apply(stats, prop.value.body); 2473 } 2474 } 2475 for (var i = 0; !abort && i < stats.length; i++) { 2476 stats[i].transform(tt); 2477 } 2478 for (var i = 0; !abort && i < fields.length; i++) { 2479 var prop = fields[i]; 2480 prop.value = prop.value.transform(tt); 2481 } 2482 return node; 2483 } 2484 // Scan object only in a for-in/of statement 2485 if (node instanceof AST_ForEnumeration) { 2486 node.object = node.object.transform(tt); 2487 abort = true; 2488 return node; 2489 } 2490 // Scan first case expression only in a switch statement 2491 if (node instanceof AST_Switch) { 2492 node.expression = node.expression.transform(tt); 2493 for (var i = 0; !abort && i < node.body.length; i++) { 2494 var branch = node.body[i]; 2495 if (branch instanceof AST_Case) { 2496 if (!hit) { 2497 if (branch !== hit_stack[hit_index]) continue; 2498 hit_index++; 2499 } 2500 branch.expression = branch.expression.transform(tt); 2501 if (!replace_all) break; 2502 scan_rhs = false; 2503 } 2504 } 2505 abort = true; 2506 return node; 2507 } 2508 } 2509 2510 function is_direct_assignment(node, parent) { 2511 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node; 2512 if (parent instanceof AST_DefaultValue) return parent.name === node; 2513 if (parent instanceof AST_DestructuredArray) return true; 2514 if (parent instanceof AST_DestructuredKeyVal) return parent.value === node; 2515 } 2516 2517 function should_stop(node, parent) { 2518 if (node === rvalue) return true; 2519 if (parent instanceof AST_For) { 2520 if (node !== parent.init) return true; 2521 } 2522 if (node instanceof AST_Assign) { 2523 return node.operator != "=" && lhs.equals(node.left); 2524 } 2525 if (node instanceof AST_Call) { 2526 if (!(lhs instanceof AST_PropAccess)) return false; 2527 if (!lhs.equals(node.expression)) return false; 2528 return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this()); 2529 } 2530 if (node instanceof AST_Class) return !compressor.has_directive("use strict"); 2531 if (node instanceof AST_Debugger) return true; 2532 if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name; 2533 if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node; 2534 if (node instanceof AST_DWLoop) return true; 2535 if (node instanceof AST_LoopControl) return true; 2536 if (node instanceof AST_Try) return true; 2537 if (node instanceof AST_With) return true; 2538 return false; 2539 } 2540 2541 function should_stop_ref(node, parent) { 2542 if (!(node instanceof AST_SymbolRef)) return false; 2543 if (node.is_declared(compressor)) { 2544 if (node.fixed_value()) return false; 2545 if (can_drop_symbol(node)) { 2546 return !(parent instanceof AST_PropAccess && parent.expression === node) 2547 && is_arguments(node.definition()); 2548 } 2549 } else if (is_direct_assignment(node, parent)) { 2550 return false; 2551 } 2552 if (!replace_all) return true; 2553 scan_rhs = false; 2554 return false; 2555 } 2556 2557 function in_conditional(node, parent) { 2558 if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)]; 2559 if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator]; 2560 if (parent instanceof AST_Call) return parent.optional && parent.expression !== node; 2561 if (parent instanceof AST_Case) return parent.expression !== node; 2562 if (parent instanceof AST_Conditional) return parent.condition !== node; 2563 if (parent instanceof AST_If) return parent.condition !== node; 2564 if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node; 2565 } 2566 2567 function is_last_node(node, parent) { 2568 if (node instanceof AST_Await) return true; 2569 if (node.TYPE == "Binary") return !can_drop_op(node.operator, node.right, compressor); 2570 if (node instanceof AST_Call) { 2571 var def, fn = node.expression; 2572 if (fn instanceof AST_SymbolRef) { 2573 def = fn.definition(); 2574 fn = fn.fixed_value(); 2575 } 2576 if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor); 2577 if (def && recursive_ref(compressor, def, fn)) return true; 2578 if (fn.collapse_scanning) return false; 2579 fn.collapse_scanning = true; 2580 var replace = can_replace; 2581 can_replace = false; 2582 var after = stop_after; 2583 var if_hit = stop_if_hit; 2584 for (var i = 0; !abort && i < fn.argnames.length; i++) { 2585 if (arg_may_throw(reject, fn.argnames[i], node.args[i])) abort = true; 2586 } 2587 if (!abort) { 2588 if (fn.rest && arg_may_throw(reject, fn.rest, make_node(AST_Array, node, { 2589 elements: node.args.slice(i), 2590 }))) { 2591 abort = true; 2592 } else if (is_arrow(fn) && fn.value) { 2593 fn.value.transform(scanner); 2594 } else for (var i = 0; !abort && i < fn.body.length; i++) { 2595 var stat = fn.body[i]; 2596 if (stat instanceof AST_Return) { 2597 if (stat.value) stat.value.transform(scanner); 2598 break; 2599 } 2600 stat.transform(scanner); 2601 } 2602 } 2603 stop_if_hit = if_hit; 2604 stop_after = after; 2605 can_replace = replace; 2606 fn.collapse_scanning = false; 2607 if (!abort) return false; 2608 abort = false; 2609 return true; 2610 } 2611 if (node instanceof AST_Class) { 2612 if (!in_try) return false; 2613 var base = node.extends; 2614 if (!base) return false; 2615 if (base instanceof AST_SymbolRef) base = base.fixed_value(); 2616 return !safe_for_extends(base); 2617 } 2618 if (node instanceof AST_Exit) { 2619 if (in_try) { 2620 if (in_try.bfinally) return true; 2621 if (in_try.bcatch && node instanceof AST_Throw) return true; 2622 } 2623 return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs); 2624 } 2625 if (node instanceof AST_Function) { 2626 return compressor.option("ie") && node.name && lvalues.has(node.name.name); 2627 } 2628 if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent); 2629 if (node instanceof AST_PropAccess) { 2630 if (side_effects) return true; 2631 var exp = node.expression; 2632 if (exp instanceof AST_SymbolRef && is_arguments(exp.definition())) return true; 2633 if (compressor.option("unsafe")) { 2634 if (is_undeclared_ref(exp) && global_names[exp.name]) return false; 2635 if (is_static_fn(exp)) return false; 2636 } 2637 if (!well_defined) return true; 2638 if (value_def) return false; 2639 if (!in_try && lhs_local) return false; 2640 if (node.optional) return false; 2641 return exp.may_throw_on_access(compressor); 2642 } 2643 if (node instanceof AST_Spread) return true; 2644 if (node instanceof AST_SymbolRef) { 2645 if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent); 2646 if (side_effects && may_modify(node)) return true; 2647 var def = node.definition(); 2648 return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node); 2649 } 2650 if (node instanceof AST_Template) return !node.is_expr_pure(compressor); 2651 if (node instanceof AST_VarDef) { 2652 if (check_destructured(node.name)) return true; 2653 return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) { 2654 return node instanceof AST_SymbolDeclaration 2655 && (lvalues.has(node.name) || side_effects && may_modify(node)); 2656 }, true); 2657 } 2658 if (node instanceof AST_Yield) return true; 2659 var sym = is_lhs(node.left, node); 2660 if (!sym) return false; 2661 if (sym instanceof AST_PropAccess) return true; 2662 if (check_destructured(sym)) return true; 2663 return sym.match_symbol(function(node) { 2664 return node instanceof AST_SymbolRef 2665 && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition())); 2666 }, true); 2667 2668 function reject(node) { 2669 node.transform(scanner); 2670 return abort; 2671 } 2672 } 2673 2674 function arg_may_throw(reject, node, value) { 2675 if (node instanceof AST_DefaultValue) { 2676 return reject(node.value) 2677 || arg_may_throw(reject, node.name, node.value) 2678 || !is_undefined(value) && arg_may_throw(reject, node.name, value); 2679 } 2680 if (!value) return !(node instanceof AST_Symbol); 2681 if (node instanceof AST_Destructured) { 2682 if (node.rest && arg_may_throw(reject, node.rest)) return true; 2683 if (node instanceof AST_DestructuredArray) { 2684 if (value instanceof AST_Array) return !all(node.elements, function(element, index) { 2685 return !arg_may_throw(reject, element, value[index]); 2686 }); 2687 if (!value.is_string(compressor)) return true; 2688 return !all(node.elements, function(element) { 2689 return !arg_may_throw(reject, element); 2690 }); 2691 } 2692 if (node instanceof AST_DestructuredObject) { 2693 if (value.may_throw_on_access(compressor)) return true; 2694 return !all(node.properties, function(prop) { 2695 if (prop.key instanceof AST_Node && reject(prop.key)) return false; 2696 return !arg_may_throw(reject, prop.value); 2697 }); 2698 } 2699 } 2700 } 2701 2702 function extract_args() { 2703 if (in_iife_single === false) return; 2704 var iife = compressor.parent(), fn = compressor.self(); 2705 if (in_iife_single === undefined) { 2706 if (!(fn instanceof AST_LambdaExpression) 2707 || is_generator(fn) 2708 || fn.uses_arguments 2709 || fn.pinned() 2710 || !(iife instanceof AST_Call) 2711 || iife.expression !== fn 2712 || !all(iife.args, function(arg) { 2713 return !(arg instanceof AST_Spread); 2714 })) { 2715 in_iife_single = false; 2716 return; 2717 } 2718 if (!is_iife_single(iife)) return; 2719 in_iife_single = true; 2720 } 2721 var fn_strict = fn.in_strict_mode(compressor) 2722 && !fn.parent_scope.resolve(true).in_strict_mode(compressor); 2723 var has_await; 2724 if (is_async(fn)) { 2725 has_await = function(node) { 2726 return node instanceof AST_Symbol && node.name == "await"; 2727 }; 2728 iife_in_try = true; 2729 } else { 2730 has_await = function(node) { 2731 return node instanceof AST_Await && !tw.find_parent(AST_Scope); 2732 }; 2733 if (iife_in_try === undefined) iife_in_try = find_try(compressor, 1, iife, null, true, true); 2734 } 2735 var arg_scope = null; 2736 var tw = new TreeWalker(function(node, descend) { 2737 if (!arg) return true; 2738 if (has_await(node) || node instanceof AST_Yield) { 2739 arg = null; 2740 return true; 2741 } 2742 if (node instanceof AST_ObjectIdentity) { 2743 if (fn_strict || !arg_scope) arg = null; 2744 return true; 2745 } 2746 if (node instanceof AST_SymbolRef) { 2747 var def; 2748 if (node.in_arg && !is_safe_lexical(node.definition()) 2749 || (def = fn.variables.get(node.name)) && def !== node.definition()) { 2750 arg = null; 2751 } 2752 return true; 2753 } 2754 if (node instanceof AST_Scope && !is_arrow(node)) { 2755 var save_scope = arg_scope; 2756 arg_scope = node; 2757 descend(); 2758 arg_scope = save_scope; 2759 return true; 2760 } 2761 }); 2762 args = iife.args.slice(); 2763 var len = args.length; 2764 var names = new Dictionary(); 2765 for (var i = fn.argnames.length; --i >= 0;) { 2766 var sym = fn.argnames[i]; 2767 var arg = args[i]; 2768 var value = null; 2769 if (sym instanceof AST_DefaultValue) { 2770 value = sym.value; 2771 sym = sym.name; 2772 args[len + i] = value; 2773 } 2774 if (sym instanceof AST_Destructured) { 2775 if (iife_in_try && arg_may_throw(function(node) { 2776 return node.has_side_effects(compressor); 2777 }, sym, arg)) { 2778 candidates.length = 0; 2779 break; 2780 } 2781 args[len + i] = fn.argnames[i]; 2782 continue; 2783 } 2784 if (names.has(sym.name)) continue; 2785 names.set(sym.name, true); 2786 if (value) arg = is_undefined(arg) ? value : null; 2787 if (!arg && !value) { 2788 arg = make_node(AST_Undefined, sym).transform(compressor); 2789 } else if (arg instanceof AST_Lambda && arg.pinned()) { 2790 arg = null; 2791 } else if (arg) { 2792 arg.walk(tw); 2793 } 2794 if (!arg) continue; 2795 var candidate = make_node(AST_VarDef, sym, { 2796 name: sym, 2797 value: arg, 2798 }); 2799 candidate.name_index = i; 2800 candidate.arg_index = value ? len + i : i; 2801 candidates.unshift([ candidate ]); 2802 } 2803 if (fn.rest) args.push(fn.rest); 2804 } 2805 2806 function extract_candidates(expr, unused) { 2807 hit_stack.push(expr); 2808 if (expr instanceof AST_Array) { 2809 expr.elements.forEach(function(node) { 2810 extract_candidates(node, unused); 2811 }); 2812 } else if (expr instanceof AST_Assign) { 2813 var lhs = expr.left; 2814 if (!(lhs instanceof AST_Destructured)) candidates.push(hit_stack.slice()); 2815 extract_candidates(lhs); 2816 extract_candidates(expr.right); 2817 if (lhs instanceof AST_SymbolRef && expr.operator == "=") { 2818 assignments.set(lhs.name, (assignments.get(lhs.name) || 0) + 1); 2819 } 2820 } else if (expr instanceof AST_Await) { 2821 extract_candidates(expr.expression, unused); 2822 } else if (expr instanceof AST_Binary) { 2823 var lazy = lazy_op[expr.operator]; 2824 if (unused 2825 && lazy 2826 && expr.operator != "??" 2827 && expr.right instanceof AST_Assign 2828 && expr.right.operator == "=" 2829 && !(expr.right.left instanceof AST_Destructured)) { 2830 candidates.push(hit_stack.slice()); 2831 } 2832 extract_candidates(expr.left, !lazy && unused); 2833 extract_candidates(expr.right, unused); 2834 } else if (expr instanceof AST_Call) { 2835 extract_candidates(expr.expression); 2836 expr.args.forEach(extract_candidates); 2837 } else if (expr instanceof AST_Case) { 2838 extract_candidates(expr.expression); 2839 } else if (expr instanceof AST_Conditional) { 2840 extract_candidates(expr.condition); 2841 extract_candidates(expr.consequent, unused); 2842 extract_candidates(expr.alternative, unused); 2843 } else if (expr instanceof AST_Definitions) { 2844 expr.definitions.forEach(extract_candidates); 2845 } else if (expr instanceof AST_Dot) { 2846 extract_candidates(expr.expression); 2847 } else if (expr instanceof AST_DWLoop) { 2848 extract_candidates(expr.condition); 2849 if (!(expr.body instanceof AST_Block)) { 2850 extract_candidates(expr.body); 2851 } 2852 } else if (expr instanceof AST_Exit) { 2853 if (expr.value) extract_candidates(expr.value); 2854 } else if (expr instanceof AST_For) { 2855 if (expr.init) extract_candidates(expr.init, true); 2856 if (expr.condition) extract_candidates(expr.condition); 2857 if (expr.step) extract_candidates(expr.step, true); 2858 if (!(expr.body instanceof AST_Block)) { 2859 extract_candidates(expr.body); 2860 } 2861 } else if (expr instanceof AST_ForEnumeration) { 2862 extract_candidates(expr.object); 2863 if (!(expr.body instanceof AST_Block)) { 2864 extract_candidates(expr.body); 2865 } 2866 } else if (expr instanceof AST_If) { 2867 extract_candidates(expr.condition); 2868 if (!(expr.body instanceof AST_Block)) { 2869 extract_candidates(expr.body); 2870 } 2871 if (expr.alternative && !(expr.alternative instanceof AST_Block)) { 2872 extract_candidates(expr.alternative); 2873 } 2874 } else if (expr instanceof AST_Object) { 2875 expr.properties.forEach(function(prop) { 2876 hit_stack.push(prop); 2877 if (prop.key instanceof AST_Node) extract_candidates(prop.key); 2878 if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value, unused); 2879 hit_stack.pop(); 2880 }); 2881 } else if (expr instanceof AST_Sequence) { 2882 var end = expr.expressions.length - (unused ? 0 : 1); 2883 expr.expressions.forEach(function(node, index) { 2884 extract_candidates(node, index < end); 2885 }); 2886 } else if (expr instanceof AST_SimpleStatement) { 2887 extract_candidates(expr.body, true); 2888 } else if (expr instanceof AST_Spread) { 2889 extract_candidates(expr.expression); 2890 } else if (expr instanceof AST_Sub) { 2891 extract_candidates(expr.expression); 2892 extract_candidates(expr.property); 2893 } else if (expr instanceof AST_Switch) { 2894 extract_candidates(expr.expression); 2895 expr.body.forEach(extract_candidates); 2896 } else if (expr instanceof AST_Unary) { 2897 if (UNARY_POSTFIX[expr.operator]) { 2898 candidates.push(hit_stack.slice()); 2899 } else { 2900 extract_candidates(expr.expression); 2901 } 2902 } else if (expr instanceof AST_VarDef) { 2903 if (expr.name instanceof AST_SymbolVar) { 2904 if (expr.value) { 2905 var def = expr.name.definition(); 2906 if (def.references.length > def.replaced) { 2907 candidates.push(hit_stack.slice()); 2908 } 2909 } else { 2910 declare_only.set(expr.name.name, (declare_only.get(expr.name.name) || 0) + 1); 2911 } 2912 } 2913 if (expr.value) extract_candidates(expr.value); 2914 } else if (expr instanceof AST_Yield) { 2915 if (expr.expression) extract_candidates(expr.expression); 2916 } 2917 hit_stack.pop(); 2918 } 2919 2920 function find_stop(node, level) { 2921 var parent = scanner.parent(level); 2922 if (parent instanceof AST_Array) return node; 2923 if (parent instanceof AST_Assign) return node; 2924 if (parent instanceof AST_Await) return node; 2925 if (parent instanceof AST_Binary) return node; 2926 if (parent instanceof AST_Call) return node; 2927 if (parent instanceof AST_Case) return node; 2928 if (parent instanceof AST_Conditional) return node; 2929 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1); 2930 if (parent instanceof AST_Exit) return node; 2931 if (parent instanceof AST_If) return node; 2932 if (parent instanceof AST_IterationStatement) return node; 2933 if (parent instanceof AST_ObjectProperty) return node; 2934 if (parent instanceof AST_PropAccess) return node; 2935 if (parent instanceof AST_Sequence) { 2936 return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1); 2937 } 2938 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1); 2939 if (parent instanceof AST_Spread) return node; 2940 if (parent instanceof AST_Switch) return node; 2941 if (parent instanceof AST_Unary) return node; 2942 if (parent instanceof AST_VarDef) return node; 2943 if (parent instanceof AST_Yield) return node; 2944 return null; 2945 } 2946 2947 function find_stop_logical(parent, op, level) { 2948 var node; 2949 do { 2950 node = parent; 2951 parent = scanner.parent(++level); 2952 } while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op 2953 || parent instanceof AST_Binary && parent.operator == op); 2954 return node; 2955 } 2956 2957 function find_stop_expr(expr, cont, node, parent, level) { 2958 var replace = can_replace; 2959 can_replace = false; 2960 var after = stop_after; 2961 var if_hit = stop_if_hit; 2962 var stack = scanner.stack; 2963 scanner.stack = [ parent ]; 2964 expr.transform(scanner); 2965 scanner.stack = stack; 2966 stop_if_hit = if_hit; 2967 stop_after = after; 2968 can_replace = replace; 2969 if (abort) { 2970 abort = false; 2971 return node; 2972 } 2973 return cont(parent, level + 1); 2974 } 2975 2976 function find_stop_value(node, level) { 2977 var parent = scanner.parent(level); 2978 if (parent instanceof AST_Array) return find_stop_value(parent, level + 1); 2979 if (parent instanceof AST_Assign) { 2980 if (may_throw(parent)) return node; 2981 if (parent.left.match_symbol(function(ref) { 2982 return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name); 2983 })) return node; 2984 var op; 2985 if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) { 2986 return find_stop_value(parent, level + 1); 2987 } 2988 return find_stop_logical(parent, op, level); 2989 } 2990 if (parent instanceof AST_Await) return find_stop_value(parent, level + 1); 2991 if (parent instanceof AST_Binary) { 2992 var op; 2993 if (parent.left === node || !lazy_op[op = parent.operator]) { 2994 return find_stop_value(parent, level + 1); 2995 } 2996 return find_stop_logical(parent, op, level); 2997 } 2998 if (parent instanceof AST_Call) return parent; 2999 if (parent instanceof AST_Case) { 3000 if (parent.expression !== node) return node; 3001 return find_stop_value(parent, level + 1); 3002 } 3003 if (parent instanceof AST_Conditional) { 3004 if (parent.condition !== node) return node; 3005 return find_stop_value(parent, level + 1); 3006 } 3007 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1); 3008 if (parent instanceof AST_Do) return node; 3009 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1); 3010 if (parent instanceof AST_For) { 3011 if (parent.init !== node && parent.condition !== node) return node; 3012 return find_stop_value(parent, level + 1); 3013 } 3014 if (parent instanceof AST_ForEnumeration) { 3015 if (parent.init !== node) return node; 3016 return find_stop_value(parent, level + 1); 3017 } 3018 if (parent instanceof AST_If) { 3019 if (parent.condition !== node) return node; 3020 return find_stop_value(parent, level + 1); 3021 } 3022 if (parent instanceof AST_ObjectProperty) { 3023 var obj = scanner.parent(level + 1); 3024 return all(obj.properties, function(prop) { 3025 return prop instanceof AST_ObjectKeyVal; 3026 }) ? find_stop_value(obj, level + 2) : obj; 3027 } 3028 if (parent instanceof AST_PropAccess) { 3029 var exp = parent.expression; 3030 return exp === node ? find_stop_value(parent, level + 1) : node; 3031 } 3032 if (parent instanceof AST_Sequence) { 3033 return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1); 3034 } 3035 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1); 3036 if (parent instanceof AST_Spread) return find_stop_value(parent, level + 1); 3037 if (parent instanceof AST_Switch) { 3038 if (parent.expression !== node) return node; 3039 return find_stop_value(parent, level + 1); 3040 } 3041 if (parent instanceof AST_Unary) { 3042 if (parent.operator == "delete") return node; 3043 return find_stop_value(parent, level + 1); 3044 } 3045 if (parent instanceof AST_VarDef) return parent.name.match_symbol(function(sym) { 3046 return sym instanceof AST_SymbolDeclaration && (lhs.name == sym.name || value_def.name == sym.name); 3047 }) ? node : find_stop_value(parent, level + 1); 3048 if (parent instanceof AST_While) { 3049 if (parent.condition !== node) return node; 3050 return find_stop_value(parent, level + 1); 3051 } 3052 if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1); 3053 return null; 3054 } 3055 3056 function find_stop_unused(node, level) { 3057 var parent = scanner.parent(level); 3058 if (is_last_node(node, parent)) return node; 3059 if (in_conditional(node, parent)) return node; 3060 if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1); 3061 if (parent instanceof AST_Assign) return check_assignment(parent.left); 3062 if (parent instanceof AST_Await) return node; 3063 if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1); 3064 if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1); 3065 if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1); 3066 if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1); 3067 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1); 3068 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1); 3069 if (parent instanceof AST_If) return find_stop_unused(parent, level + 1); 3070 if (parent instanceof AST_IterationStatement) return node; 3071 if (parent instanceof AST_ObjectProperty) { 3072 var obj = scanner.parent(level + 1); 3073 return all(obj.properties, function(prop) { 3074 return prop instanceof AST_ObjectKeyVal; 3075 }) ? find_stop_unused(obj, level + 2) : obj; 3076 } 3077 if (parent instanceof AST_PropAccess) { 3078 var exp = parent.expression; 3079 if (exp === node) return find_stop_unused(parent, level + 1); 3080 return find_stop_expr(exp, find_stop_unused, node, parent, level); 3081 } 3082 if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1); 3083 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1); 3084 if (parent instanceof AST_Spread) return node; 3085 if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1); 3086 if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1); 3087 if (parent instanceof AST_VarDef) return check_assignment(parent.name); 3088 if (parent instanceof AST_Yield) return node; 3089 return null; 3090 3091 function check_assignment(lhs) { 3092 if (may_throw(parent)) return node; 3093 if (lhs !== node && lhs instanceof AST_Destructured) { 3094 return find_stop_expr(lhs, find_stop_unused, node, parent, level); 3095 } 3096 return find_stop_unused(parent, level + 1); 3097 } 3098 } 3099 3100 function mangleable_var(rhs) { 3101 if (force_single) { 3102 force_single = false; 3103 return; 3104 } 3105 if (remaining < 1) return; 3106 rhs = rhs.tail_node(); 3107 var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs; 3108 if (!(value instanceof AST_SymbolRef)) return; 3109 var def = value.definition(); 3110 if (def.undeclared) return; 3111 if (is_arguments(def)) return; 3112 if (value !== rhs) { 3113 if (is_lhs_read_only(value, compressor)) return; 3114 var referenced = def.references.length - def.replaced; 3115 if (referenced < 2) return; 3116 var expr = candidate.clone(); 3117 expr[expr instanceof AST_Assign ? "right" : "value"] = value; 3118 if (candidate.name_index >= 0) { 3119 expr.name_index = candidate.name_index; 3120 expr.arg_index = candidate.arg_index; 3121 } 3122 candidate = expr; 3123 } 3124 return value_def = def; 3125 } 3126 3127 function remaining_refs(def) { 3128 return def.references.length - def.replaced - (assignments.get(def.name) || 0); 3129 } 3130 3131 function get_lhs(expr) { 3132 if (expr instanceof AST_Assign) { 3133 var lhs = expr.left; 3134 if (!(lhs instanceof AST_SymbolRef)) return lhs; 3135 var def = lhs.definition(); 3136 if (scope.uses_arguments && is_funarg(def)) return lhs; 3137 if (compressor.exposed(def)) return lhs; 3138 remaining = remaining_refs(def); 3139 if (def.fixed && lhs.fixed) { 3140 var matches = def.references.filter(function(ref) { 3141 return ref.fixed === lhs.fixed; 3142 }).length - 1; 3143 if (matches < remaining) { 3144 remaining = matches; 3145 assign_pos = 0; 3146 verify_ref = true; 3147 } 3148 } 3149 if (expr.operator == "=") mangleable_var(expr.right); 3150 return lhs; 3151 } 3152 if (expr instanceof AST_Binary) return expr.right.left; 3153 if (expr instanceof AST_Unary) return expr.expression; 3154 if (expr instanceof AST_VarDef) { 3155 var lhs = expr.name; 3156 var def = lhs.definition(); 3157 if (def.const_redefs) return; 3158 if (!member(lhs, def.orig)) return; 3159 if (scope.uses_arguments && is_funarg(def)) return; 3160 var declared = def.orig.length - def.eliminated - (declare_only.get(def.name) || 0); 3161 remaining = remaining_refs(def); 3162 if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) { 3163 if (!ref.fixed) return true; 3164 if (!ref.fixed.assigns) return true; 3165 var assign = ref.fixed.assigns[0]; 3166 return assign === lhs || get_rvalue(assign) === expr.value; 3167 }).length); 3168 if (declared > 1 && !(lhs instanceof AST_SymbolFunarg)) { 3169 mangleable_var(expr.value); 3170 return make_node(AST_SymbolRef, lhs); 3171 } 3172 if (mangleable_var(expr.value) || remaining == 1 && !compressor.exposed(def)) { 3173 return make_node(AST_SymbolRef, lhs); 3174 } 3175 return; 3176 } 3177 } 3178 3179 function get_rvalue(expr) { 3180 if (expr instanceof AST_Assign) return expr.right; 3181 if (expr instanceof AST_Binary) { 3182 var node = expr.clone(); 3183 node.right = expr.right.right; 3184 return node; 3185 } 3186 if (expr instanceof AST_VarDef) return expr.value; 3187 } 3188 3189 function invariant(expr) { 3190 if (expr instanceof AST_Array) return false; 3191 if (expr instanceof AST_Binary && lazy_op[expr.operator]) { 3192 return invariant(expr.left) && invariant(expr.right); 3193 } 3194 if (expr instanceof AST_Call) return false; 3195 if (expr instanceof AST_Conditional) { 3196 return invariant(expr.consequent) && invariant(expr.alternative); 3197 } 3198 if (expr instanceof AST_Object) return false; 3199 return !expr.has_side_effects(compressor); 3200 } 3201 3202 function foldable(expr) { 3203 if (expr instanceof AST_Assign && expr.right.single_use) return; 3204 var lhs_ids = Object.create(null); 3205 var marker = new TreeWalker(function(node) { 3206 if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true; 3207 }); 3208 while (expr instanceof AST_Assign && expr.operator == "=") { 3209 expr.left.walk(marker); 3210 expr = expr.right; 3211 } 3212 if (expr instanceof AST_ObjectIdentity) return rhs_exact_match; 3213 if (expr instanceof AST_SymbolRef) { 3214 var value = expr.evaluate(compressor); 3215 if (value === expr) return rhs_exact_match; 3216 return rhs_fuzzy_match(value, rhs_exact_match); 3217 } 3218 if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false); 3219 if (expr.is_constant()) { 3220 var ev = expr.evaluate(compressor); 3221 if (!(ev instanceof AST_Node)) return rhs_fuzzy_match(ev, rhs_exact_match); 3222 } 3223 if (!(lhs instanceof AST_SymbolRef)) return false; 3224 if (!invariant(expr)) return false; 3225 var circular; 3226 expr.walk(new TreeWalker(function(node) { 3227 if (circular) return true; 3228 if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true; 3229 })); 3230 return !circular && rhs_exact_match; 3231 3232 function rhs_exact_match(node) { 3233 return expr.equals(node); 3234 } 3235 } 3236 3237 function rhs_fuzzy_match(value, fallback) { 3238 return function(node, tw) { 3239 if (tw.in_boolean_context()) { 3240 if (value && node.is_truthy() && !node.has_side_effects(compressor)) { 3241 return true; 3242 } 3243 if (node.is_constant()) { 3244 var ev = node.evaluate(compressor); 3245 if (!(ev instanceof AST_Node)) return !ev == !value; 3246 } 3247 } 3248 return fallback(node); 3249 }; 3250 } 3251 3252 function clear_write_only(assign) { 3253 while (assign.write_only) { 3254 assign.write_only = false; 3255 if (!(assign instanceof AST_Assign)) break; 3256 assign = assign.right; 3257 } 3258 } 3259 3260 function update_symbols(value, node) { 3261 var scope = node.scope || find_scope(scanner) || block_scope; 3262 value.walk(new TreeWalker(function(node) { 3263 if (node instanceof AST_BlockScope) return true; 3264 if (node instanceof AST_Symbol) node.scope = scope; 3265 })); 3266 } 3267 3268 function may_be_global(node) { 3269 if (node instanceof AST_SymbolRef) { 3270 node = node.fixed_value(); 3271 if (!node) return true; 3272 } 3273 if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right); 3274 return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity; 3275 } 3276 3277 function get_lvalues(expr) { 3278 var lvalues = new Dictionary(); 3279 if (expr instanceof AST_VarDef) { 3280 if (!expr.name.definition().fixed) well_defined = false; 3281 lvalues.add(expr.name.name, lhs); 3282 } 3283 var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict"); 3284 var scan_toplevel = scope instanceof AST_Toplevel; 3285 var tw = new TreeWalker(function(node) { 3286 var value; 3287 if (node instanceof AST_SymbolRef) { 3288 value = node.fixed_value(); 3289 if (!value) { 3290 value = node; 3291 var def = node.definition(); 3292 var escaped = node.fixed && node.fixed.escaped || def.escaped; 3293 if (!def.undeclared 3294 && (def.assignments || !escaped || escaped.cross_scope) 3295 && (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) { 3296 well_defined = false; 3297 } 3298 } 3299 } else if (node instanceof AST_ObjectIdentity) { 3300 value = node; 3301 } 3302 if (value) { 3303 lvalues.add(node.name, is_modified(compressor, tw, node, value, 0)); 3304 } else if (node instanceof AST_Lambda) { 3305 for (var level = 0, parent, child = node; parent = tw.parent(level++); child = parent) { 3306 if (parent instanceof AST_Assign) { 3307 if (parent.left === child) break; 3308 if (parent.operator == "=") continue; 3309 if (lazy_op[parent.operator.slice(0, -1)]) continue; 3310 break; 3311 } 3312 if (parent instanceof AST_Binary) { 3313 if (lazy_op[parent.operator]) continue; 3314 break; 3315 } 3316 if (parent instanceof AST_Call) return; 3317 if (parent instanceof AST_Scope) return; 3318 if (parent instanceof AST_Sequence) { 3319 if (parent.tail_node() === child) continue; 3320 break; 3321 } 3322 if (parent instanceof AST_Template) { 3323 if (parent.tag) return; 3324 break; 3325 } 3326 } 3327 node.enclosed.forEach(function(def) { 3328 if (def.scope !== node) enclosed.set(def.name, true); 3329 }); 3330 return true; 3331 } else if (find_arguments && node instanceof AST_Sub) { 3332 scope.each_argname(function(argname) { 3333 if (!compressor.option("reduce_vars") || argname.definition().assignments) { 3334 if (!argname.definition().fixed) well_defined = false; 3335 lvalues.add(argname.name, true); 3336 } 3337 }); 3338 find_arguments = false; 3339 } 3340 if (!scan_toplevel) return; 3341 if (node.TYPE == "Call") { 3342 if (modify_toplevel) return; 3343 var exp = node.expression; 3344 if (exp instanceof AST_PropAccess) return; 3345 if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return; 3346 modify_toplevel = true; 3347 } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) { 3348 if (node === lhs && !(expr instanceof AST_Unary)) { 3349 modify_toplevel = true; 3350 } else { 3351 read_toplevel = true; 3352 } 3353 } 3354 }); 3355 expr.walk(tw); 3356 return lvalues; 3357 } 3358 3359 function remove_candidate(expr) { 3360 var value = rvalue === rhs_value ? null : make_sequence(rhs_value, rhs_value.expressions.slice(0, -1)); 3361 var index = expr.name_index; 3362 if (index >= 0) { 3363 var args, argname = scope.argnames[index]; 3364 if (argname instanceof AST_DefaultValue) { 3365 scope.argnames[index] = argname = argname.clone(); 3366 argname.value = value || make_node(AST_Number, argname, { value: 0 }); 3367 } else if ((args = compressor.parent().args)[index]) { 3368 scope.argnames[index] = argname.clone(); 3369 args[index] = value || make_node(AST_Number, args[index], { value: 0 }); 3370 } 3371 return; 3372 } 3373 var end = hit_stack.length - 1; 3374 var last = hit_stack[end]; 3375 if (last instanceof AST_VarDef || hit_stack[end - 1].body === last) end--; 3376 var tt = new TreeTransformer(function(node, descend, in_list) { 3377 if (hit) return node; 3378 if (node !== hit_stack[hit_index]) return node; 3379 hit_index++; 3380 if (hit_index <= end) return handle_custom_scan_order(node, tt); 3381 hit = true; 3382 if (node instanceof AST_Definitions) { 3383 declare_only.set(last.name.name, (declare_only.get(last.name.name) || 0) + 1); 3384 if (value_def) value_def.replaced++; 3385 var defns = node.definitions; 3386 var index = defns.indexOf(last); 3387 var defn = last.clone(); 3388 defn.value = null; 3389 if (!value) { 3390 node.definitions[index] = defn; 3391 return node; 3392 } 3393 var body = [ make_node(AST_SimpleStatement, value, { body: value }) ]; 3394 if (index > 0) { 3395 var head = node.clone(); 3396 head.definitions = defns.slice(0, index); 3397 body.unshift(head); 3398 node = node.clone(); 3399 node.definitions = defns.slice(index); 3400 } 3401 body.push(node); 3402 node.definitions[0] = defn; 3403 return in_list ? List.splice(body) : make_node(AST_BlockStatement, node, { body: body }); 3404 } 3405 if (!value) return in_list ? List.skip : null; 3406 return is_statement(node) ? make_node(AST_SimpleStatement, value, { body: value }) : value; 3407 }, function(node, in_list) { 3408 if (node instanceof AST_For) return patch_for_init(node, in_list); 3409 return patch_sequence(node, tt); 3410 }); 3411 abort = false; 3412 hit = false; 3413 hit_index = 0; 3414 if (!(statements[stat_index] = statements[stat_index].transform(tt))) statements.splice(stat_index, 1); 3415 } 3416 3417 function patch_sequence(node, tt) { 3418 if (node instanceof AST_Sequence) switch (node.expressions.length) { 3419 case 0: return null; 3420 case 1: return maintain_this_binding(tt.parent(), node, node.expressions[0]); 3421 } 3422 } 3423 3424 function is_lhs_local(lhs) { 3425 var sym = root_expr(lhs); 3426 if (!(sym instanceof AST_SymbolRef)) return false; 3427 if (sym.definition().scope.resolve() !== scope) return false; 3428 if (!in_loop) return true; 3429 if (compound) return false; 3430 if (candidate instanceof AST_Unary) return false; 3431 var lvalue = lvalues.get(sym.name); 3432 return !lvalue || lvalue[0] === lhs; 3433 } 3434 3435 function value_has_side_effects() { 3436 if (candidate instanceof AST_Unary) return false; 3437 return rvalue.has_side_effects(compressor); 3438 } 3439 3440 function replace_all_symbols(expr) { 3441 if (expr instanceof AST_Unary) return false; 3442 if (side_effects) return false; 3443 if (value_def) return true; 3444 if (!(lhs instanceof AST_SymbolRef)) return false; 3445 var referenced; 3446 if (expr instanceof AST_VarDef) { 3447 referenced = 1; 3448 } else if (expr.operator == "=") { 3449 referenced = 2; 3450 } else { 3451 return false; 3452 } 3453 var def = lhs.definition(); 3454 if (def.references.length - def.replaced == referenced) return true; 3455 if (!def.fixed) return false; 3456 if (!lhs.fixed) return false; 3457 var assigns = lhs.fixed.assigns; 3458 var matched = 0; 3459 if (!all(def.references, function(ref, index) { 3460 var fixed = ref.fixed; 3461 if (!fixed) return false; 3462 if (fixed.to_binary || fixed.to_prefix) return false; 3463 if (fixed === lhs.fixed) { 3464 matched++; 3465 return true; 3466 } 3467 return assigns && fixed.assigns && assigns[0] !== fixed.assigns[0]; 3468 })) return false; 3469 if (matched != referenced) return false; 3470 verify_ref = true; 3471 return true; 3472 } 3473 3474 function symbol_in_lvalues(sym, parent) { 3475 var lvalue = lvalues.get(sym.name); 3476 if (!lvalue || all(lvalue, function(lhs) { 3477 return !lhs; 3478 })) return; 3479 if (lvalue[0] !== lhs) return true; 3480 scan_rhs = false; 3481 } 3482 3483 function may_modify(sym) { 3484 var def = sym.definition(); 3485 if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false; 3486 if (def.scope.resolve() !== scope) return true; 3487 if (modify_toplevel && compressor.exposed(def)) return true; 3488 return !all(def.references, function(ref) { 3489 return ref.scope.resolve(true) === scope; 3490 }); 3491 } 3492 3493 function side_effects_external(node, lhs) { 3494 if (node instanceof AST_Assign) return side_effects_external(node.left, true); 3495 if (node instanceof AST_Unary) return side_effects_external(node.expression, true); 3496 if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value); 3497 if (lhs) { 3498 if (node instanceof AST_Dot) return side_effects_external(node.expression, true); 3499 if (node instanceof AST_Sub) return side_effects_external(node.expression, true); 3500 if (node instanceof AST_SymbolRef) return node.definition().scope.resolve() !== scope; 3501 } 3502 return false; 3503 } 3504 } 3505 3506 function eliminate_spurious_blocks(statements) { 3507 var changed = false, seen_dirs = []; 3508 for (var i = 0; i < statements.length;) { 3509 var stat = statements[i]; 3510 if (stat instanceof AST_BlockStatement) { 3511 if (all(stat.body, safe_to_trim)) { 3512 changed = true; 3513 eliminate_spurious_blocks(stat.body); 3514 [].splice.apply(statements, [i, 1].concat(stat.body)); 3515 i += stat.body.length; 3516 continue; 3517 } 3518 } 3519 if (stat instanceof AST_Directive) { 3520 if (member(stat.value, seen_dirs)) { 3521 changed = true; 3522 statements.splice(i, 1); 3523 continue; 3524 } 3525 seen_dirs.push(stat.value); 3526 } 3527 if (stat instanceof AST_EmptyStatement) { 3528 changed = true; 3529 statements.splice(i, 1); 3530 continue; 3531 } 3532 i++; 3533 } 3534 return changed; 3535 } 3536 3537 function handle_if_return(statements, compressor) { 3538 var changed = false; 3539 var parent = compressor.parent(); 3540 var self = compressor.self(); 3541 var declare_only, jump, merge_jump; 3542 var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self; 3543 var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences"); 3544 var drop_return_void = !(in_try && in_try.bfinally && in_async_generator(scope)); 3545 var multiple_if_returns = has_multiple_if_returns(statements); 3546 for (var i = statements.length; --i >= 0;) { 3547 var stat = statements[i]; 3548 var j = next_index(i); 3549 var next = statements[j]; 3550 3551 if (in_lambda && declare_only && !next && stat instanceof AST_Return 3552 && drop_return_void && !(self instanceof AST_SwitchBranch)) { 3553 var body = stat.value; 3554 if (!body) { 3555 changed = true; 3556 statements.splice(i, 1); 3557 continue; 3558 } 3559 var tail = body.tail_node(); 3560 if (is_undefined(tail)) { 3561 changed = true; 3562 if (body instanceof AST_UnaryPrefix) { 3563 body = body.expression; 3564 } else if (tail instanceof AST_UnaryPrefix) { 3565 body = body.clone(); 3566 body.expressions[body.expressions.length - 1] = tail.expression; 3567 } 3568 statements[i] = make_node(AST_SimpleStatement, stat, { body: body }); 3569 continue; 3570 } 3571 } 3572 3573 if (stat instanceof AST_If) { 3574 var ab = aborts(stat.body); 3575 // if (foo()) { bar(); return; } else baz(); moo(); ---> if (foo()) bar(); else { baz(); moo(); } 3576 if (can_merge_flow(ab)) { 3577 if (ab.label) remove(ab.label.thedef.references, ab); 3578 changed = true; 3579 stat = stat.clone(); 3580 stat.body = make_node(AST_BlockStatement, stat, { 3581 body: as_statement_array_with_return(stat.body, ab), 3582 }); 3583 stat.alternative = make_node(AST_BlockStatement, stat, { 3584 body: as_statement_array(stat.alternative).concat(extract_functions(merge_jump, jump)), 3585 }); 3586 adjust_refs(ab.value, merge_jump); 3587 statements[i] = stat; 3588 statements[i] = stat.transform(compressor); 3589 continue; 3590 } 3591 // if (foo()) { bar(); return x; } return y; ---> if (!foo()) return y; bar(); return x; 3592 if (ab && !stat.alternative && next instanceof AST_Jump) { 3593 var cond = stat.condition; 3594 var preference = i + 1 == j && stat.body instanceof AST_BlockStatement; 3595 cond = best_of_expression(cond, cond.negate(compressor), preference); 3596 if (cond !== stat.condition) { 3597 changed = true; 3598 stat = stat.clone(); 3599 stat.condition = cond; 3600 var body = stat.body; 3601 stat.body = make_node(AST_BlockStatement, next, { 3602 body: extract_functions(true, null, j + 1), 3603 }); 3604 statements.splice(i, 1, stat, body); 3605 // proceed further only if `TreeWalker.stack` is in a consistent state 3606 // https://github.com/mishoo/UglifyJS/issues/5595 3607 // https://github.com/mishoo/UglifyJS/issues/5597 3608 if (!in_lambda || self instanceof AST_Block && self.body === statements) { 3609 statements[i] = stat.transform(compressor); 3610 } 3611 continue; 3612 } 3613 } 3614 var alt = aborts(stat.alternative); 3615 // if (foo()) bar(); else { baz(); return; } moo(); ---> if (foo()) { bar(); moo(); } else baz(); 3616 if (can_merge_flow(alt)) { 3617 if (alt.label) remove(alt.label.thedef.references, alt); 3618 changed = true; 3619 stat = stat.clone(); 3620 stat.body = make_node(AST_BlockStatement, stat.body, { 3621 body: as_statement_array(stat.body).concat(extract_functions(merge_jump, jump)), 3622 }); 3623 stat.alternative = make_node(AST_BlockStatement, stat.alternative, { 3624 body: as_statement_array_with_return(stat.alternative, alt), 3625 }); 3626 adjust_refs(alt.value, merge_jump); 3627 statements[i] = stat; 3628 statements[i] = stat.transform(compressor); 3629 continue; 3630 } 3631 if (compressor.option("typeofs")) { 3632 if (ab && !alt) { 3633 var stats = make_node(AST_BlockStatement, self, { body: statements.slice(i + 1) }); 3634 mark_locally_defined(stat.condition, null, stats); 3635 } 3636 if (!ab && alt) { 3637 var stats = make_node(AST_BlockStatement, self, { body: statements.slice(i + 1) }); 3638 mark_locally_defined(stat.condition, stats); 3639 } 3640 } 3641 } 3642 3643 if (stat instanceof AST_If && stat.body instanceof AST_Return) { 3644 var value = stat.body.value; 3645 var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool; 3646 // if (foo()) return x; return y; ---> return foo() ? x : y; 3647 if (!stat.alternative && next instanceof AST_Return 3648 && (drop_return_void || !value == !next.value)) { 3649 changed = true; 3650 stat = stat.clone(); 3651 stat.alternative = make_node(AST_BlockStatement, next, { 3652 body: extract_functions(true, null, j + 1), 3653 }); 3654 statements[i] = stat; 3655 statements[i] = stat.transform(compressor); 3656 continue; 3657 } 3658 // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined; 3659 // if (foo()) return bar() ? x : void 0; ---> return foo() && bar() ? x : void 0; 3660 // if (foo()) return bar() ? void 0 : x; ---> return !foo() || bar() ? void 0 : x; 3661 if (in_lambda && declare_only && !next && !stat.alternative && (in_bool 3662 || value && multiple_if_returns 3663 || value instanceof AST_Conditional && (is_undefined(value.consequent, compressor) 3664 || is_undefined(value.alternative, compressor)))) { 3665 changed = true; 3666 stat = stat.clone(); 3667 stat.alternative = make_node(AST_Return, stat, { value: null }); 3668 statements[i] = stat; 3669 statements[i] = stat.transform(compressor); 3670 continue; 3671 } 3672 // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e; 3673 // 3674 // if sequences is not enabled, this can lead to an endless loop (issue #866). 3675 // however, with sequences on this helps producing slightly better output for 3676 // the example code. 3677 var prev, prev_stat; 3678 if (chain_if_returns && !stat.alternative 3679 && (!(prev_stat = statements[prev = prev_index(i)]) && in_iife 3680 || prev_stat instanceof AST_If && prev_stat.body instanceof AST_Return) 3681 && (!next ? !declare_only 3682 : next instanceof AST_SimpleStatement && next_index(j) == statements.length)) { 3683 changed = true; 3684 var exprs = []; 3685 stat = stat.clone(); 3686 exprs.push(stat.condition); 3687 stat.condition = make_sequence(stat, exprs); 3688 stat.alternative = make_node(AST_BlockStatement, self, { 3689 body: extract_functions().concat(make_node(AST_Return, self, { value: null })), 3690 }); 3691 statements[i] = stat.transform(compressor); 3692 i = prev + 1; 3693 continue; 3694 } 3695 } 3696 3697 if (stat instanceof AST_Break || stat instanceof AST_Exit) { 3698 jump = stat; 3699 continue; 3700 } 3701 3702 if (declare_only && jump && jump === next) eliminate_returns(stat); 3703 } 3704 return changed; 3705 3706 function has_multiple_if_returns(statements) { 3707 var n = 0; 3708 for (var i = statements.length; --i >= 0;) { 3709 var stat = statements[i]; 3710 if (stat instanceof AST_If && stat.body instanceof AST_Return) { 3711 if (++n > 1) return true; 3712 } 3713 } 3714 return false; 3715 } 3716 3717 function match_target(target) { 3718 return last_of(compressor, function(node) { 3719 return node === target; 3720 }); 3721 } 3722 3723 function match_return(ab, exact) { 3724 if (!jump) return false; 3725 if (jump.TYPE != ab.TYPE) return false; 3726 var value = ab.value; 3727 if (!value) return false; 3728 var equals = jump.equals(ab); 3729 if (!equals && value instanceof AST_Sequence) { 3730 value = value.tail_node(); 3731 if (jump.value && jump.value.equals(value)) equals = 2; 3732 } 3733 if (!equals && !exact && jump.value instanceof AST_Sequence) { 3734 if (jump.value.tail_node().equals(value)) equals = 3; 3735 } 3736 return equals; 3737 } 3738 3739 function can_drop_abort(ab) { 3740 if (ab instanceof AST_Exit) { 3741 if (merge_jump = match_return(ab)) return true; 3742 if (!in_lambda) return false; 3743 if (!(ab instanceof AST_Return)) return false; 3744 var value = ab.value; 3745 if (value && !is_undefined(value.tail_node())) return false; 3746 if (!(self instanceof AST_SwitchBranch)) return true; 3747 if (!jump) return false; 3748 if (jump instanceof AST_Exit && jump.value) return false; 3749 merge_jump = 4; 3750 return true; 3751 } 3752 if (!(ab instanceof AST_LoopControl)) return false; 3753 if (self instanceof AST_SwitchBranch) { 3754 if (jump instanceof AST_Exit) { 3755 if (!in_lambda) return false; 3756 if (jump.value) return false; 3757 merge_jump = true; 3758 } else if (jump) { 3759 if (compressor.loopcontrol_target(jump) !== parent) return false; 3760 merge_jump = true; 3761 } else if (jump === false) { 3762 return false; 3763 } 3764 } 3765 var lct = compressor.loopcontrol_target(ab); 3766 if (ab instanceof AST_Continue) return match_target(loop_body(lct)); 3767 if (lct instanceof AST_IterationStatement) return false; 3768 return match_target(lct); 3769 } 3770 3771 function can_merge_flow(ab) { 3772 merge_jump = false; 3773 if (!can_drop_abort(ab)) return false; 3774 for (var j = statements.length; --j > i;) { 3775 var stat = statements[j]; 3776 if (stat instanceof AST_DefClass) { 3777 if (stat.name.definition().preinit) return false; 3778 } else if (stat instanceof AST_Const || stat instanceof AST_Let) { 3779 if (!all(stat.definitions, function(defn) { 3780 return !defn.name.match_symbol(function(node) { 3781 return node instanceof AST_SymbolDeclaration && node.definition().preinit; 3782 }); 3783 })) return false; 3784 } 3785 } 3786 return true; 3787 } 3788 3789 function extract_functions(mode, stop, end) { 3790 var defuns = []; 3791 var lexical = false; 3792 var start = i + 1; 3793 if (!mode) { 3794 end = statements.length; 3795 jump = null; 3796 } else if (stop) { 3797 end = statements.lastIndexOf(stop); 3798 } else { 3799 stop = statements[end]; 3800 if (stop !== jump) jump = false; 3801 } 3802 var tail = statements.splice(start, end - start).filter(function(stat) { 3803 if (stat instanceof AST_LambdaDefinition) { 3804 defuns.push(stat); 3805 return false; 3806 } 3807 if (is_lexical_definition(stat)) lexical = true; 3808 return true; 3809 }); 3810 if (mode === 3) { 3811 tail.push(make_node(AST_SimpleStatement, stop.value, { 3812 body: make_sequence(stop.value, stop.value.expressions.slice(0, -1)), 3813 })); 3814 stop.value = stop.value.tail_node(); 3815 } 3816 [].push.apply(lexical ? tail : statements, defuns); 3817 return tail; 3818 } 3819 3820 function trim_return(value, mode) { 3821 if (value) switch (mode) { 3822 case 4: 3823 return value; 3824 case 3: 3825 if (!(value instanceof AST_Sequence)) break; 3826 case 2: 3827 return make_sequence(value, value.expressions.slice(0, -1)); 3828 } 3829 } 3830 3831 function as_statement_array_with_return(node, ab) { 3832 var body = as_statement_array(node); 3833 var block = body, last; 3834 while ((last = block[block.length - 1]) !== ab) { 3835 block = last.body; 3836 } 3837 block.pop(); 3838 var value = ab.value; 3839 if (merge_jump) value = trim_return(value, merge_jump); 3840 if (value) block.push(make_node(AST_SimpleStatement, value, { body: value })); 3841 return body; 3842 } 3843 3844 function adjust_refs(value, mode) { 3845 if (!mode) return; 3846 if (!value) return; 3847 switch (mode) { 3848 case 4: 3849 return; 3850 case 3: 3851 case 2: 3852 value = value.tail_node(); 3853 } 3854 merge_expression(value, jump.value); 3855 } 3856 3857 function next_index(i) { 3858 declare_only = true; 3859 for (var j = i; ++j < statements.length;) { 3860 var stat = statements[j]; 3861 if (is_declaration(stat)) continue; 3862 if (stat instanceof AST_Var) { 3863 declare_only = false; 3864 continue; 3865 } 3866 break; 3867 } 3868 return j; 3869 } 3870 3871 function prev_index(i) { 3872 for (var j = i; --j >= 0;) { 3873 var stat = statements[j]; 3874 if (stat instanceof AST_Var) continue; 3875 if (is_declaration(stat)) continue; 3876 break; 3877 } 3878 return j; 3879 } 3880 3881 function eliminate_returns(stat, keep_throws, in_block) { 3882 if (stat instanceof AST_Exit) { 3883 var mode = !(keep_throws && stat instanceof AST_Throw) && match_return(stat, true); 3884 if (mode) { 3885 changed = true; 3886 var value = trim_return(stat.value, mode); 3887 if (value) return make_node(AST_SimpleStatement, value, { body: value }); 3888 return in_block ? null : make_node(AST_EmptyStatement, stat); 3889 } 3890 } else if (stat instanceof AST_If) { 3891 stat.body = eliminate_returns(stat.body, keep_throws); 3892 if (stat.alternative) stat.alternative = eliminate_returns(stat.alternative, keep_throws); 3893 } else if (stat instanceof AST_LabeledStatement) { 3894 stat.body = eliminate_returns(stat.body, keep_throws); 3895 } else if (stat instanceof AST_Try) { 3896 if (!stat.bfinally || !jump.value || jump.value.is_constant()) { 3897 if (stat.bcatch) eliminate_returns(stat.bcatch, keep_throws); 3898 var trimmed = eliminate_returns(stat.body.pop(), true, true); 3899 if (trimmed) stat.body.push(trimmed); 3900 } 3901 } else if (stat instanceof AST_Block && !(stat instanceof AST_Scope || stat instanceof AST_Switch)) { 3902 var trimmed = eliminate_returns(stat.body.pop(), keep_throws, true); 3903 if (trimmed) stat.body.push(trimmed); 3904 } 3905 return stat; 3906 } 3907 } 3908 3909 function eliminate_dead_code(statements, compressor) { 3910 var has_quit; 3911 var self = compressor.self(); 3912 if (self instanceof AST_Catch) { 3913 self = compressor.parent(); 3914 } else if (self instanceof AST_LabeledStatement) { 3915 self = self.body; 3916 } 3917 for (var i = 0, n = 0, len = statements.length; i < len; i++) { 3918 var stat = statements[i]; 3919 if (stat instanceof AST_LoopControl) { 3920 var lct = compressor.loopcontrol_target(stat); 3921 if (loop_body(lct) !== self 3922 || stat instanceof AST_Break && lct instanceof AST_IterationStatement) { 3923 statements[n++] = stat; 3924 } else if (stat.label) { 3925 remove(stat.label.thedef.references, stat); 3926 } 3927 } else { 3928 statements[n++] = stat; 3929 } 3930 if (aborts(stat)) { 3931 has_quit = statements.slice(i + 1); 3932 break; 3933 } 3934 } 3935 statements.length = n; 3936 if (has_quit) has_quit.forEach(function(stat) { 3937 extract_declarations_from_unreachable_code(compressor, stat, statements); 3938 }); 3939 return statements.length != len; 3940 } 3941 3942 function trim_awaits(statements, compressor) { 3943 if (!in_lambda || in_try && in_try.bfinally) return; 3944 var changed = false; 3945 for (var index = statements.length; --index >= 0;) { 3946 var stat = statements[index]; 3947 if (!(stat instanceof AST_SimpleStatement)) break; 3948 var node = stat.body; 3949 if (!(node instanceof AST_Await)) break; 3950 var exp = node.expression; 3951 if (!needs_enqueuing(compressor, exp)) break; 3952 changed = true; 3953 exp = exp.drop_side_effect_free(compressor, true); 3954 if (exp) { 3955 stat.body = exp; 3956 break; 3957 } 3958 } 3959 statements.length = index + 1; 3960 return changed; 3961 } 3962 3963 function inline_iife(statements, compressor) { 3964 var changed = false; 3965 var index = statements.length - 1; 3966 if (in_lambda && index >= 0) { 3967 var no_return = in_try && in_try.bfinally && in_async_generator(scope); 3968 var inlined = statements[index].try_inline(compressor, block_scope, no_return); 3969 if (inlined) { 3970 statements[index--] = inlined; 3971 changed = true; 3972 } 3973 } 3974 var loop = in_loop && in_try && in_try.bfinally ? "try" : in_loop; 3975 for (; index >= 0; index--) { 3976 var inlined = statements[index].try_inline(compressor, block_scope, true, loop); 3977 if (!inlined) continue; 3978 statements[index] = inlined; 3979 changed = true; 3980 } 3981 return changed; 3982 } 3983 3984 function sequencesize(statements, compressor) { 3985 if (statements.length < 2) return; 3986 var seq = [], n = 0; 3987 function push_seq() { 3988 if (!seq.length) return; 3989 var body = make_sequence(seq[0], seq); 3990 statements[n++] = make_node(AST_SimpleStatement, body, { body: body }); 3991 seq = []; 3992 } 3993 for (var i = 0, len = statements.length; i < len; i++) { 3994 var stat = statements[i]; 3995 if (stat instanceof AST_SimpleStatement) { 3996 if (seq.length >= compressor.sequences_limit) push_seq(); 3997 merge_sequence(seq, stat.body); 3998 } else if (is_declaration(stat)) { 3999 statements[n++] = stat; 4000 } else { 4001 push_seq(); 4002 statements[n++] = stat; 4003 } 4004 } 4005 push_seq(); 4006 statements.length = n; 4007 return n != len; 4008 } 4009 4010 function to_simple_statement(block, decls) { 4011 if (!(block instanceof AST_BlockStatement)) return block; 4012 var stat = null; 4013 for (var i = 0; i < block.body.length; i++) { 4014 var line = block.body[i]; 4015 if (line instanceof AST_Var && declarations_only(line)) { 4016 decls.push(line); 4017 } else if (stat || is_lexical_definition(line)) { 4018 return false; 4019 } else { 4020 stat = line; 4021 } 4022 } 4023 return stat; 4024 } 4025 4026 function sequencesize_2(statements, compressor) { 4027 var changed = false, n = 0, prev; 4028 for (var i = 0; i < statements.length; i++) { 4029 var stat = statements[i]; 4030 if (prev) { 4031 if (stat instanceof AST_Exit) { 4032 if (stat.value || !in_async_generator(scope)) { 4033 stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).optimize(compressor); 4034 } 4035 } else if (stat instanceof AST_For) { 4036 if (!(stat.init instanceof AST_Definitions)) { 4037 var abort = false; 4038 prev.body.walk(new TreeWalker(function(node) { 4039 if (abort || node instanceof AST_Scope) return true; 4040 if (node instanceof AST_Binary && node.operator == "in") { 4041 abort = true; 4042 return true; 4043 } 4044 })); 4045 if (!abort) { 4046 if (stat.init) stat.init = cons_seq(stat.init); 4047 else { 4048 stat.init = prev.body; 4049 n--; 4050 changed = true; 4051 } 4052 } 4053 } 4054 } else if (stat instanceof AST_ForIn) { 4055 if (!is_lexical_definition(stat.init)) stat.object = cons_seq(stat.object); 4056 } else if (stat instanceof AST_If) { 4057 stat.condition = cons_seq(stat.condition); 4058 } else if (stat instanceof AST_Switch) { 4059 stat.expression = cons_seq(stat.expression); 4060 } else if (stat instanceof AST_With) { 4061 stat.expression = cons_seq(stat.expression); 4062 } 4063 } 4064 if (compressor.option("conditionals") && stat instanceof AST_If) { 4065 var decls = []; 4066 var body = to_simple_statement(stat.body, decls); 4067 var alt = to_simple_statement(stat.alternative, decls); 4068 if (body !== false && alt !== false && decls.length > 0) { 4069 var len = decls.length; 4070 decls.push(make_node(AST_If, stat, { 4071 condition: stat.condition, 4072 body: body || make_node(AST_EmptyStatement, stat.body), 4073 alternative: alt, 4074 })); 4075 decls.unshift(n, 1); 4076 [].splice.apply(statements, decls); 4077 i += len; 4078 n += len + 1; 4079 prev = null; 4080 changed = true; 4081 continue; 4082 } 4083 } 4084 statements[n++] = stat; 4085 prev = stat instanceof AST_SimpleStatement ? stat : null; 4086 } 4087 statements.length = n; 4088 return changed; 4089 4090 function cons_seq(right) { 4091 n--; 4092 changed = true; 4093 var left = prev.body; 4094 return make_sequence(left, [ left, right ]); 4095 } 4096 } 4097 4098 function extract_exprs(body) { 4099 if (body instanceof AST_Assign) return [ body ]; 4100 if (body instanceof AST_Sequence) return body.expressions.slice(); 4101 } 4102 4103 function join_assigns(defn, body, keep) { 4104 var exprs = extract_exprs(body); 4105 if (!exprs) return; 4106 keep = keep || 0; 4107 var trimmed = false; 4108 for (var i = exprs.length - keep; --i >= 0;) { 4109 var expr = exprs[i]; 4110 if (!can_trim(expr)) continue; 4111 var tail; 4112 if (expr.left instanceof AST_SymbolRef) { 4113 tail = exprs.slice(i + 1); 4114 } else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) { 4115 tail = exprs.slice(i + 1); 4116 var flattened = expr.clone(); 4117 expr = expr.left.expression; 4118 flattened.left = flattened.left.clone(); 4119 flattened.left.expression = expr.left.clone(); 4120 tail.unshift(flattened); 4121 } else { 4122 continue; 4123 } 4124 if (tail.length == 0) continue; 4125 if (!trim_assigns(expr.left, expr.right, tail)) continue; 4126 trimmed = true; 4127 exprs = exprs.slice(0, i).concat(expr, tail); 4128 } 4129 if (defn instanceof AST_Definitions) { 4130 for (var i = defn.definitions.length; --i >= 0;) { 4131 var def = defn.definitions[i]; 4132 if (!def.value) continue; 4133 if (trim_assigns(def.name, def.value, exprs)) trimmed = true; 4134 if (merge_conditional_assignments(def, exprs, keep)) trimmed = true; 4135 break; 4136 } 4137 if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true; 4138 } 4139 return trimmed && exprs; 4140 4141 function can_trim(node) { 4142 return node instanceof AST_Assign && node.operator == "="; 4143 } 4144 } 4145 4146 function merge_assigns(prev, defn) { 4147 if (!(prev instanceof AST_SimpleStatement)) return; 4148 if (declarations_only(defn)) return; 4149 var exprs = extract_exprs(prev.body); 4150 if (!exprs) return; 4151 var definitions = []; 4152 if (!join_var_assign(definitions, exprs.reverse(), 0)) return; 4153 defn.definitions = definitions.reverse().concat(defn.definitions); 4154 return exprs.reverse(); 4155 } 4156 4157 function merge_conditional_assignments(var_def, exprs, keep) { 4158 if (!compressor.option("conditionals")) return; 4159 if (var_def.name instanceof AST_Destructured) return; 4160 var trimmed = false; 4161 var def = var_def.name.definition(); 4162 while (exprs.length > keep) { 4163 var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]); 4164 if (!cond) break; 4165 var_def.value = cond; 4166 exprs.shift(); 4167 trimmed = true; 4168 } 4169 return trimmed; 4170 } 4171 4172 function join_var_assign(definitions, exprs, keep) { 4173 var trimmed = false; 4174 while (exprs.length > keep) { 4175 var expr = exprs[0]; 4176 if (!(expr instanceof AST_Assign)) break; 4177 if (expr.operator != "=") break; 4178 var lhs = expr.left; 4179 if (!(lhs instanceof AST_SymbolRef)) break; 4180 if (is_undeclared_ref(lhs)) break; 4181 if (lhs.scope.resolve() !== scope) break; 4182 var def = lhs.definition(); 4183 if (def.scope !== scope) break; 4184 if (def.orig.length > def.eliminated + 1) break; 4185 if (def.orig[0].TYPE != "SymbolVar") break; 4186 var name = make_node(AST_SymbolVar, lhs); 4187 definitions.push(make_node(AST_VarDef, expr, { 4188 name: name, 4189 value: expr.right, 4190 })); 4191 def.orig.push(name); 4192 def.replaced++; 4193 exprs.shift(); 4194 trimmed = true; 4195 } 4196 return trimmed; 4197 } 4198 4199 function trim_assigns(name, value, exprs) { 4200 var names = new Dictionary(); 4201 names.set(name.name, true); 4202 while (value instanceof AST_Assign && value.operator == "=") { 4203 if (value.left instanceof AST_SymbolRef) names.set(value.left.name, true); 4204 value = value.right; 4205 } 4206 if (!(value instanceof AST_Object)) return; 4207 var trimmed = false; 4208 do { 4209 if (!try_join(exprs[0])) break; 4210 exprs.shift(); 4211 trimmed = true; 4212 } while (exprs.length); 4213 return trimmed; 4214 4215 function try_join(node) { 4216 if (!(node instanceof AST_Assign)) return; 4217 if (node.operator != "=") return; 4218 if (!(node.left instanceof AST_PropAccess)) return; 4219 var sym = node.left.expression; 4220 if (!(sym instanceof AST_SymbolRef)) return; 4221 if (!names.has(sym.name)) return; 4222 if (!node.right.is_constant_expression(scope)) return; 4223 var prop = node.left.property; 4224 if (prop instanceof AST_Node) { 4225 if (try_join(prop)) prop = node.left.property = prop.right.clone(); 4226 prop = prop.evaluate(compressor); 4227 } 4228 if (prop instanceof AST_Node) return; 4229 prop = "" + prop; 4230 var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) { 4231 var key = node.key; 4232 return typeof key == "string" && key != prop && key != "__proto__"; 4233 } : function(node) { 4234 var key = node.key; 4235 if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) { 4236 return typeof key == "string" && key != prop; 4237 } 4238 return key !== "__proto__"; 4239 }; 4240 if (!all(value.properties, diff)) return; 4241 value.properties.push(make_node(AST_ObjectKeyVal, node, { 4242 key: prop, 4243 value: node.right, 4244 })); 4245 return true; 4246 } 4247 } 4248 4249 function join_consecutive_vars(statements) { 4250 var changed = false, defs; 4251 for (var i = 0, j = -1; i < statements.length; i++) { 4252 var stat = statements[i]; 4253 var prev = statements[j]; 4254 if (stat instanceof AST_Definitions) { 4255 if (prev && prev.TYPE == stat.TYPE) { 4256 prev.definitions = prev.definitions.concat(stat.definitions); 4257 changed = true; 4258 } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) { 4259 defs.definitions = defs.definitions.concat(stat.definitions); 4260 changed = true; 4261 } else if (stat instanceof AST_Var) { 4262 var exprs = merge_assigns(prev, stat); 4263 if (exprs) { 4264 if (exprs.length) { 4265 prev.body = make_sequence(prev, exprs); 4266 j++; 4267 } 4268 changed = true; 4269 } else { 4270 j++; 4271 } 4272 statements[j] = defs = stat; 4273 } else { 4274 statements[++j] = stat; 4275 } 4276 continue; 4277 } else if (stat instanceof AST_Exit) { 4278 stat.value = join_assigns_expr(stat.value); 4279 } else if (stat instanceof AST_For) { 4280 var exprs = join_assigns(prev, stat.init); 4281 if (exprs) { 4282 changed = true; 4283 stat.init = exprs.length ? make_sequence(stat.init, exprs) : null; 4284 } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) { 4285 if (stat.init) { 4286 prev.definitions = prev.definitions.concat(stat.init.definitions); 4287 } 4288 stat = stat.clone(); 4289 defs = stat.init = prev; 4290 statements[j] = merge_defns(stat); 4291 changed = true; 4292 continue; 4293 } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) { 4294 defs.definitions = defs.definitions.concat(stat.init.definitions); 4295 stat.init = null; 4296 changed = true; 4297 } else if (stat.init instanceof AST_Var) { 4298 defs = stat.init; 4299 exprs = merge_assigns(prev, stat.init); 4300 if (exprs) { 4301 changed = true; 4302 if (exprs.length == 0) { 4303 statements[j] = merge_defns(stat); 4304 continue; 4305 } 4306 prev.body = make_sequence(prev, exprs); 4307 } 4308 } 4309 } else if (stat instanceof AST_ForEnumeration) { 4310 if (defs && defs.TYPE == stat.init.TYPE) { 4311 var defns = defs.definitions.slice(); 4312 stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) { 4313 defns.push(make_node(AST_VarDef, name, { 4314 name: name, 4315 value: null, 4316 })); 4317 name.definition().references.push(ref); 4318 }); 4319 defs.definitions = defns; 4320 changed = true; 4321 } 4322 stat.object = join_assigns_expr(stat.object); 4323 } else if (stat instanceof AST_If) { 4324 stat.condition = join_assigns_expr(stat.condition); 4325 } else if (stat instanceof AST_SimpleStatement) { 4326 var exprs = join_assigns(prev, stat.body), next; 4327 if (exprs) { 4328 changed = true; 4329 if (!exprs.length) continue; 4330 stat.body = make_sequence(stat.body, exprs); 4331 } else if (prev instanceof AST_Definitions 4332 && (next = statements[i + 1]) 4333 && prev.TYPE == next.TYPE 4334 && (next = next.definitions[0]).value) { 4335 changed = true; 4336 next.value = make_sequence(stat, [ stat.body, next.value ]); 4337 continue; 4338 } 4339 } else if (stat instanceof AST_Switch) { 4340 stat.expression = join_assigns_expr(stat.expression); 4341 } else if (stat instanceof AST_With) { 4342 stat.expression = join_assigns_expr(stat.expression); 4343 } 4344 statements[++j] = defs ? merge_defns(stat) : stat; 4345 } 4346 statements.length = j + 1; 4347 return changed; 4348 4349 function join_assigns_expr(value) { 4350 var exprs = join_assigns(prev, value, 1); 4351 if (!exprs) return value; 4352 changed = true; 4353 var tail = value.tail_node(); 4354 if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left); 4355 return make_sequence(value, exprs); 4356 } 4357 4358 function merge_defns(stat) { 4359 return stat.transform(new TreeTransformer(function(node, descend, in_list) { 4360 if (node instanceof AST_Definitions) { 4361 if (defs === node) return node; 4362 if (defs.TYPE != node.TYPE) return node; 4363 var parent = this.parent(); 4364 if (parent instanceof AST_ForEnumeration && parent.init === node) return node; 4365 if (!declarations_only(node)) return node; 4366 defs.definitions = defs.definitions.concat(node.definitions); 4367 changed = true; 4368 if (parent instanceof AST_For && parent.init === node) return null; 4369 return in_list ? List.skip : make_node(AST_EmptyStatement, node); 4370 } 4371 if (node instanceof AST_ExportDeclaration) return node; 4372 if (node instanceof AST_Scope) return node; 4373 if (!is_statement(node)) return node; 4374 })); 4375 } 4376 } 4377 } 4378 4379 function extract_declarations_from_unreachable_code(compressor, stat, target) { 4380 var block; 4381 var dropped = false; 4382 stat.walk(new TreeWalker(function(node, descend) { 4383 if (node instanceof AST_DefClass) { 4384 node.extends = null; 4385 node.properties = []; 4386 push(node); 4387 return true; 4388 } 4389 if (node instanceof AST_Definitions) { 4390 var defns = []; 4391 if (node.remove_initializers(compressor, defns)) { 4392 AST_Node.warn("Dropping initialization in unreachable code [{start}]", node); 4393 } 4394 if (defns.length > 0) { 4395 node.definitions = defns; 4396 push(node); 4397 } 4398 return true; 4399 } 4400 if (node instanceof AST_LambdaDefinition) { 4401 push(node); 4402 return true; 4403 } 4404 if (node instanceof AST_Scope) return true; 4405 if (node instanceof AST_BlockScope) { 4406 var save = block; 4407 block = []; 4408 descend(); 4409 if (block.required) { 4410 target.push(make_node(AST_BlockStatement, stat, { body: block })); 4411 } else if (block.length) { 4412 [].push.apply(target, block); 4413 } 4414 block = save; 4415 return true; 4416 } 4417 if (!(node instanceof AST_LoopControl)) dropped = true; 4418 })); 4419 if (dropped) AST_Node.warn("Dropping unreachable code [{start}]", stat); 4420 4421 function push(node) { 4422 if (block) { 4423 block.push(node); 4424 if (!safe_to_trim(node)) block.required = true; 4425 } else { 4426 target.push(node); 4427 } 4428 } 4429 } 4430 4431 function is_undefined(node, compressor) { 4432 return node == null 4433 || node.is_undefined 4434 || node instanceof AST_Undefined 4435 || node instanceof AST_UnaryPrefix 4436 && node.operator == "void" 4437 && !(compressor && node.expression.has_side_effects(compressor)); 4438 } 4439 4440 // in_strict_mode() 4441 // return true if scope executes in Strict Mode 4442 (function(def) { 4443 def(AST_Class, return_true); 4444 def(AST_Scope, function(compressor) { 4445 var body = this.body; 4446 for (var i = 0; i < body.length; i++) { 4447 var stat = body[i]; 4448 if (!(stat instanceof AST_Directive)) break; 4449 if (stat.value == "use strict") return true; 4450 } 4451 var parent = this.parent_scope; 4452 if (!parent) return compressor.option("module"); 4453 return parent.resolve(true).in_strict_mode(compressor); 4454 }); 4455 })(function(node, func) { 4456 node.DEFMETHOD("in_strict_mode", func); 4457 }); 4458 4459 // is_truthy() 4460 // return true if `!!node === true` 4461 (function(def) { 4462 def(AST_Node, return_false); 4463 def(AST_Array, return_true); 4464 def(AST_Assign, function() { 4465 return this.operator == "=" && this.right.is_truthy(); 4466 }); 4467 def(AST_Lambda, return_true); 4468 def(AST_Object, return_true); 4469 def(AST_RegExp, return_true); 4470 def(AST_Sequence, function() { 4471 return this.tail_node().is_truthy(); 4472 }); 4473 def(AST_SymbolRef, function() { 4474 var fixed = this.fixed_value(); 4475 if (!fixed) return false; 4476 this.is_truthy = return_false; 4477 var result = fixed.is_truthy(); 4478 delete this.is_truthy; 4479 return result; 4480 }); 4481 })(function(node, func) { 4482 node.DEFMETHOD("is_truthy", func); 4483 }); 4484 4485 // is_negative_zero() 4486 // return true if the node may represent -0 4487 (function(def) { 4488 def(AST_Node, return_true); 4489 def(AST_Array, return_false); 4490 function binary(op, left, right) { 4491 switch (op) { 4492 case "-": 4493 return left.is_negative_zero() 4494 && (!(right instanceof AST_Constant) || right.value == 0); 4495 case "&&": 4496 case "||": 4497 return left.is_negative_zero() || right.is_negative_zero(); 4498 case "*": 4499 case "/": 4500 case "%": 4501 case "**": 4502 return true; 4503 default: 4504 return false; 4505 } 4506 } 4507 def(AST_Assign, function() { 4508 var op = this.operator; 4509 if (op == "=") return this.right.is_negative_zero(); 4510 return binary(op.slice(0, -1), this.left, this.right); 4511 }); 4512 def(AST_Binary, function() { 4513 return binary(this.operator, this.left, this.right); 4514 }); 4515 def(AST_Constant, function() { 4516 return this.value == 0 && 1 / this.value < 0; 4517 }); 4518 def(AST_Lambda, return_false); 4519 def(AST_Object, return_false); 4520 def(AST_RegExp, return_false); 4521 def(AST_Sequence, function() { 4522 return this.tail_node().is_negative_zero(); 4523 }); 4524 def(AST_SymbolRef, function() { 4525 var fixed = this.fixed_value(); 4526 if (!fixed) return true; 4527 this.is_negative_zero = return_true; 4528 var result = fixed.is_negative_zero(); 4529 delete this.is_negative_zero; 4530 return result; 4531 }); 4532 def(AST_UnaryPrefix, function() { 4533 return this.operator == "+" && this.expression.is_negative_zero() 4534 || this.operator == "-"; 4535 }); 4536 })(function(node, func) { 4537 node.DEFMETHOD("is_negative_zero", func); 4538 }); 4539 4540 // may_throw_on_access() 4541 // returns true if this node may be null, undefined or contain `AST_Accessor` 4542 (function(def) { 4543 AST_Node.DEFMETHOD("may_throw_on_access", function(compressor, force) { 4544 return !compressor.option("pure_getters") || this._dot_throw(compressor, force); 4545 }); 4546 function is_strict(compressor, force) { 4547 return force || /strict/.test(compressor.option("pure_getters")); 4548 } 4549 def(AST_Node, is_strict); 4550 def(AST_Array, return_false); 4551 def(AST_Assign, function(compressor) { 4552 var op = this.operator; 4553 var sym = this.left; 4554 var rhs = this.right; 4555 if (op != "=") { 4556 return lazy_op[op.slice(0, -1)] && (sym._dot_throw(compressor) || rhs._dot_throw(compressor)); 4557 } 4558 if (!rhs._dot_throw(compressor)) return false; 4559 if (!(sym instanceof AST_SymbolRef)) return true; 4560 if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) { 4561 return rhs.right._dot_throw(compressor); 4562 } 4563 return true; 4564 }); 4565 def(AST_Binary, function(compressor) { 4566 return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor)); 4567 }); 4568 def(AST_Class, function(compressor, force) { 4569 return is_strict(compressor, force) && !all(this.properties, function(prop) { 4570 if (prop.private) return true; 4571 if (!prop.static) return true; 4572 return !(prop instanceof AST_ClassGetter || prop instanceof AST_ClassSetter); 4573 }); 4574 }); 4575 def(AST_Conditional, function(compressor) { 4576 return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor); 4577 }); 4578 def(AST_Constant, return_false); 4579 def(AST_Dot, function(compressor, force) { 4580 if (!is_strict(compressor, force)) return false; 4581 var exp = this.expression; 4582 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value(); 4583 return !(this.property == "prototype" && is_lambda(exp)); 4584 }); 4585 def(AST_Lambda, return_false); 4586 def(AST_Null, return_true); 4587 def(AST_Object, function(compressor, force) { 4588 return is_strict(compressor, force) && !all(this.properties, function(prop) { 4589 if (prop instanceof AST_ObjectGetter || prop instanceof AST_ObjectSetter) return false; 4590 return !(prop.key === "__proto__" && prop.value._dot_throw(compressor, force)); 4591 }); 4592 }); 4593 def(AST_ObjectIdentity, function(compressor, force) { 4594 return is_strict(compressor, force) && !this.scope.resolve().new; 4595 }); 4596 def(AST_Sequence, function(compressor) { 4597 return this.tail_node()._dot_throw(compressor); 4598 }); 4599 def(AST_SymbolRef, function(compressor, force) { 4600 if (this.is_undefined) return true; 4601 if (!is_strict(compressor, force)) return false; 4602 if (is_undeclared_ref(this) && this.is_declared(compressor)) return false; 4603 if (this.is_immutable()) return false; 4604 var def = this.definition(); 4605 if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) { 4606 return argname instanceof AST_SymbolFunarg; 4607 })) return def.scope.uses_arguments > 2; 4608 var fixed = this.fixed_value(true); 4609 if (!fixed) return true; 4610 this._dot_throw = return_true; 4611 if (fixed._dot_throw(compressor)) { 4612 delete this._dot_throw; 4613 return true; 4614 } 4615 this._dot_throw = return_false; 4616 return false; 4617 }); 4618 def(AST_UnaryPrefix, function() { 4619 return this.operator == "void"; 4620 }); 4621 def(AST_UnaryPostfix, return_false); 4622 def(AST_Undefined, return_true); 4623 })(function(node, func) { 4624 node.DEFMETHOD("_dot_throw", func); 4625 }); 4626 4627 (function(def) { 4628 def(AST_Node, return_false); 4629 def(AST_Array, return_true); 4630 function is_binary_defined(compressor, op, node) { 4631 switch (op) { 4632 case "&&": 4633 return node.left.is_defined(compressor) && node.right.is_defined(compressor); 4634 case "||": 4635 return node.left.is_truthy() || node.right.is_defined(compressor); 4636 case "??": 4637 return node.left.is_defined(compressor) || node.right.is_defined(compressor); 4638 default: 4639 return true; 4640 } 4641 } 4642 def(AST_Assign, function(compressor) { 4643 var op = this.operator; 4644 if (op == "=") return this.right.is_defined(compressor); 4645 return is_binary_defined(compressor, op.slice(0, -1), this); 4646 }); 4647 def(AST_Binary, function(compressor) { 4648 return is_binary_defined(compressor, this.operator, this); 4649 }); 4650 def(AST_Conditional, function(compressor) { 4651 return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor); 4652 }); 4653 def(AST_Constant, return_true); 4654 def(AST_Hole, return_false); 4655 def(AST_Lambda, return_true); 4656 def(AST_Object, return_true); 4657 def(AST_Sequence, function(compressor) { 4658 return this.tail_node().is_defined(compressor); 4659 }); 4660 def(AST_SymbolRef, function(compressor) { 4661 if (this.is_undefined) return false; 4662 if (is_undeclared_ref(this) && this.is_declared(compressor)) return true; 4663 if (this.is_immutable()) return true; 4664 var fixed = this.fixed_value(); 4665 if (!fixed) return false; 4666 this.is_defined = return_false; 4667 var result = fixed.is_defined(compressor); 4668 delete this.is_defined; 4669 return result; 4670 }); 4671 def(AST_UnaryPrefix, function() { 4672 return this.operator != "void"; 4673 }); 4674 def(AST_UnaryPostfix, return_true); 4675 def(AST_Undefined, return_false); 4676 })(function(node, func) { 4677 node.DEFMETHOD("is_defined", func); 4678 }); 4679 4680 /* -----[ boolean/negation helpers ]----- */ 4681 4682 // methods to determine whether an expression has a boolean result type 4683 (function(def) { 4684 def(AST_Node, return_false); 4685 def(AST_Assign, function(compressor) { 4686 return this.operator == "=" && this.right.is_boolean(compressor); 4687 }); 4688 var binary = makePredicate("in instanceof == != === !== < <= >= >"); 4689 def(AST_Binary, function(compressor) { 4690 return binary[this.operator] || lazy_op[this.operator] 4691 && this.left.is_boolean(compressor) 4692 && this.right.is_boolean(compressor); 4693 }); 4694 def(AST_Boolean, return_true); 4695 var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some"); 4696 def(AST_Call, function(compressor) { 4697 if (!compressor.option("unsafe")) return false; 4698 var exp = this.expression; 4699 return exp instanceof AST_Dot && (fn[exp.property] 4700 || exp.property == "test" && exp.expression instanceof AST_RegExp); 4701 }); 4702 def(AST_Conditional, function(compressor) { 4703 return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor); 4704 }); 4705 def(AST_New, return_false); 4706 def(AST_Sequence, function(compressor) { 4707 return this.tail_node().is_boolean(compressor); 4708 }); 4709 def(AST_SymbolRef, function(compressor) { 4710 var fixed = this.fixed_value(); 4711 if (!fixed) return false; 4712 this.is_boolean = return_false; 4713 var result = fixed.is_boolean(compressor); 4714 delete this.is_boolean; 4715 return result; 4716 }); 4717 var unary = makePredicate("! delete"); 4718 def(AST_UnaryPrefix, function() { 4719 return unary[this.operator]; 4720 }); 4721 })(function(node, func) { 4722 node.DEFMETHOD("is_boolean", func); 4723 }); 4724 4725 // methods to determine if an expression has a numeric result type 4726 (function(def) { 4727 def(AST_Node, return_false); 4728 var binary = makePredicate("- * / % ** & | ^ << >> >>>"); 4729 def(AST_Assign, function(compressor) { 4730 return binary[this.operator.slice(0, -1)] 4731 || this.operator == "=" && this.right.is_number(compressor); 4732 }); 4733 def(AST_Binary, function(compressor) { 4734 if (binary[this.operator]) return true; 4735 if (this.operator != "+") return false; 4736 return (this.left.is_boolean(compressor) || this.left.is_number(compressor)) 4737 && (this.right.is_boolean(compressor) || this.right.is_number(compressor)); 4738 }); 4739 var fn = makePredicate([ 4740 "charCodeAt", 4741 "getDate", 4742 "getDay", 4743 "getFullYear", 4744 "getHours", 4745 "getMilliseconds", 4746 "getMinutes", 4747 "getMonth", 4748 "getSeconds", 4749 "getTime", 4750 "getTimezoneOffset", 4751 "getUTCDate", 4752 "getUTCDay", 4753 "getUTCFullYear", 4754 "getUTCHours", 4755 "getUTCMilliseconds", 4756 "getUTCMinutes", 4757 "getUTCMonth", 4758 "getUTCSeconds", 4759 "getYear", 4760 "indexOf", 4761 "lastIndexOf", 4762 "localeCompare", 4763 "push", 4764 "search", 4765 "setDate", 4766 "setFullYear", 4767 "setHours", 4768 "setMilliseconds", 4769 "setMinutes", 4770 "setMonth", 4771 "setSeconds", 4772 "setTime", 4773 "setUTCDate", 4774 "setUTCFullYear", 4775 "setUTCHours", 4776 "setUTCMilliseconds", 4777 "setUTCMinutes", 4778 "setUTCMonth", 4779 "setUTCSeconds", 4780 "setYear", 4781 ]); 4782 def(AST_Call, function(compressor) { 4783 if (!compressor.option("unsafe")) return false; 4784 var exp = this.expression; 4785 return exp instanceof AST_Dot && (fn[exp.property] 4786 || is_undeclared_ref(exp.expression) && exp.expression.name == "Math"); 4787 }); 4788 def(AST_Conditional, function(compressor) { 4789 return this.consequent.is_number(compressor) && this.alternative.is_number(compressor); 4790 }); 4791 def(AST_New, return_false); 4792 def(AST_Number, return_true); 4793 def(AST_Sequence, function(compressor) { 4794 return this.tail_node().is_number(compressor); 4795 }); 4796 def(AST_SymbolRef, function(compressor, keep_unary) { 4797 var fixed = this.fixed_value(); 4798 if (!fixed) return false; 4799 if (keep_unary 4800 && fixed instanceof AST_UnaryPrefix 4801 && fixed.operator == "+" 4802 && fixed.expression.equals(this)) { 4803 return false; 4804 } 4805 this.is_number = return_false; 4806 var result = fixed.is_number(compressor); 4807 delete this.is_number; 4808 return result; 4809 }); 4810 var unary = makePredicate("+ - ~ ++ --"); 4811 def(AST_Unary, function() { 4812 return unary[this.operator]; 4813 }); 4814 })(function(node, func) { 4815 node.DEFMETHOD("is_number", func); 4816 }); 4817 4818 // methods to determine if an expression has a string result type 4819 (function(def) { 4820 def(AST_Node, return_false); 4821 def(AST_Assign, function(compressor) { 4822 switch (this.operator) { 4823 case "+=": 4824 if (this.left.is_string(compressor)) return true; 4825 case "=": 4826 return this.right.is_string(compressor); 4827 } 4828 }); 4829 def(AST_Binary, function(compressor) { 4830 return this.operator == "+" && 4831 (this.left.is_string(compressor) || this.right.is_string(compressor)); 4832 }); 4833 var fn = makePredicate([ 4834 "charAt", 4835 "substr", 4836 "substring", 4837 "toExponential", 4838 "toFixed", 4839 "toLowerCase", 4840 "toPrecision", 4841 "toString", 4842 "toUpperCase", 4843 "trim", 4844 ]); 4845 def(AST_Call, function(compressor) { 4846 if (!compressor.option("unsafe")) return false; 4847 var exp = this.expression; 4848 return exp instanceof AST_Dot && fn[exp.property]; 4849 }); 4850 def(AST_Conditional, function(compressor) { 4851 return this.consequent.is_string(compressor) && this.alternative.is_string(compressor); 4852 }); 4853 def(AST_Sequence, function(compressor) { 4854 return this.tail_node().is_string(compressor); 4855 }); 4856 def(AST_String, return_true); 4857 def(AST_SymbolRef, function(compressor) { 4858 var fixed = this.fixed_value(); 4859 if (!fixed) return false; 4860 this.is_string = return_false; 4861 var result = fixed.is_string(compressor); 4862 delete this.is_string; 4863 return result; 4864 }); 4865 def(AST_Template, function(compressor) { 4866 return !this.tag || is_raw_tag(compressor, this.tag); 4867 }); 4868 def(AST_UnaryPrefix, function() { 4869 return this.operator == "typeof"; 4870 }); 4871 })(function(node, func) { 4872 node.DEFMETHOD("is_string", func); 4873 }); 4874 4875 var lazy_op = makePredicate("&& || ??"); 4876 4877 (function(def) { 4878 function to_node(value, orig) { 4879 if (value instanceof AST_Node) return value.clone(true); 4880 if (Array.isArray(value)) return make_node(AST_Array, orig, { 4881 elements: value.map(function(value) { 4882 return to_node(value, orig); 4883 }) 4884 }); 4885 if (value && typeof value == "object") { 4886 var props = []; 4887 for (var key in value) if (HOP(value, key)) { 4888 props.push(make_node(AST_ObjectKeyVal, orig, { 4889 key: key, 4890 value: to_node(value[key], orig), 4891 })); 4892 } 4893 return make_node(AST_Object, orig, { properties: props }); 4894 } 4895 return make_node_from_constant(value, orig); 4896 } 4897 4898 function warn(node) { 4899 AST_Node.warn("global_defs {this} redefined [{start}]", node); 4900 } 4901 4902 AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) { 4903 if (!compressor.option("global_defs")) return this; 4904 this.figure_out_scope({ ie: compressor.option("ie") }); 4905 return this.transform(new TreeTransformer(function(node) { 4906 var def = node._find_defs(compressor, ""); 4907 if (!def) return; 4908 var level = 0, child = node, parent; 4909 while (parent = this.parent(level++)) { 4910 if (!(parent instanceof AST_PropAccess)) break; 4911 if (parent.expression !== child) break; 4912 child = parent; 4913 } 4914 if (is_lhs(child, parent)) { 4915 warn(node); 4916 return; 4917 } 4918 return def; 4919 })); 4920 }); 4921 def(AST_Node, noop); 4922 def(AST_Dot, function(compressor, suffix) { 4923 return this.expression._find_defs(compressor, "." + this.property + suffix); 4924 }); 4925 def(AST_SymbolDeclaration, function(compressor) { 4926 if (!this.definition().global) return; 4927 if (HOP(compressor.option("global_defs"), this.name)) warn(this); 4928 }); 4929 def(AST_SymbolRef, function(compressor, suffix) { 4930 if (!this.definition().global) return; 4931 var defines = compressor.option("global_defs"); 4932 var name = this.name + suffix; 4933 if (HOP(defines, name)) return to_node(defines[name], this); 4934 }); 4935 })(function(node, func) { 4936 node.DEFMETHOD("_find_defs", func); 4937 }); 4938 4939 function best_of_expression(ast1, ast2, threshold) { 4940 var delta = ast2.print_to_string().length - ast1.print_to_string().length; 4941 return delta < (threshold || 0) ? ast2 : ast1; 4942 } 4943 4944 function best_of_statement(ast1, ast2, threshold) { 4945 return best_of_expression(make_node(AST_SimpleStatement, ast1, { 4946 body: ast1, 4947 }), make_node(AST_SimpleStatement, ast2, { 4948 body: ast2, 4949 }), threshold).body; 4950 } 4951 4952 function best_of(compressor, ast1, ast2, threshold) { 4953 return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold); 4954 } 4955 4956 function convert_to_predicate(obj) { 4957 var map = Object.create(null); 4958 Object.keys(obj).forEach(function(key) { 4959 map[key] = makePredicate(obj[key]); 4960 }); 4961 return map; 4962 } 4963 4964 function skip_directives(body) { 4965 for (var i = 0; i < body.length; i++) { 4966 var stat = body[i]; 4967 if (!(stat instanceof AST_Directive)) return stat; 4968 } 4969 } 4970 4971 function arrow_first_statement() { 4972 if (this.value) return make_node(AST_Return, this.value, { value: this.value }); 4973 return skip_directives(this.body); 4974 } 4975 AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement); 4976 AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement); 4977 AST_Lambda.DEFMETHOD("first_statement", function() { 4978 return skip_directives(this.body); 4979 }); 4980 4981 AST_Lambda.DEFMETHOD("length", function() { 4982 var argnames = this.argnames; 4983 for (var i = 0; i < argnames.length; i++) { 4984 if (argnames[i] instanceof AST_DefaultValue) break; 4985 } 4986 return i; 4987 }); 4988 4989 function try_evaluate(compressor, node) { 4990 var ev = node.evaluate(compressor); 4991 if (ev === node) return node; 4992 ev = make_node_from_constant(ev, node).optimize(compressor); 4993 return best_of(compressor, node, ev, compressor.eval_threshold); 4994 } 4995 4996 var object_fns = [ 4997 "constructor", 4998 "toString", 4999 "valueOf", 5000 ]; 5001 var native_fns = convert_to_predicate({ 5002 Array: [ 5003 "indexOf", 5004 "join", 5005 "lastIndexOf", 5006 "slice", 5007 ].concat(object_fns), 5008 Boolean: object_fns, 5009 Function: object_fns, 5010 Number: [ 5011 "toExponential", 5012 "toFixed", 5013 "toPrecision", 5014 ].concat(object_fns), 5015 Object: object_fns, 5016 RegExp: [ 5017 "exec", 5018 "test", 5019 ].concat(object_fns), 5020 String: [ 5021 "charAt", 5022 "charCodeAt", 5023 "concat", 5024 "indexOf", 5025 "italics", 5026 "lastIndexOf", 5027 "match", 5028 "replace", 5029 "search", 5030 "slice", 5031 "split", 5032 "substr", 5033 "substring", 5034 "toLowerCase", 5035 "toUpperCase", 5036 "trim", 5037 ].concat(object_fns), 5038 }); 5039 var static_fns = convert_to_predicate({ 5040 Array: [ 5041 "isArray", 5042 ], 5043 Math: [ 5044 "abs", 5045 "acos", 5046 "asin", 5047 "atan", 5048 "ceil", 5049 "cos", 5050 "exp", 5051 "floor", 5052 "log", 5053 "round", 5054 "sin", 5055 "sqrt", 5056 "tan", 5057 "atan2", 5058 "pow", 5059 "max", 5060 "min", 5061 ], 5062 Number: [ 5063 "isFinite", 5064 "isNaN", 5065 ], 5066 Object: [ 5067 "create", 5068 "getOwnPropertyDescriptor", 5069 "getOwnPropertyNames", 5070 "getPrototypeOf", 5071 "isExtensible", 5072 "isFrozen", 5073 "isSealed", 5074 "keys", 5075 ], 5076 String: [ 5077 "fromCharCode", 5078 "raw", 5079 ], 5080 }); 5081 5082 function is_static_fn(node) { 5083 if (!(node instanceof AST_Dot)) return false; 5084 var expr = node.expression; 5085 if (!is_undeclared_ref(expr)) return false; 5086 var static_fn = static_fns[expr.name]; 5087 return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random"); 5088 } 5089 5090 // Accommodate when compress option evaluate=false 5091 // as well as the common constant expressions !0 and -1 5092 (function(def) { 5093 def(AST_Node, return_false); 5094 def(AST_Constant, return_true); 5095 def(AST_RegExp, return_false); 5096 var unaryPrefix = makePredicate("! ~ - + void"); 5097 def(AST_UnaryPrefix, function() { 5098 return unaryPrefix[this.operator] && this.expression instanceof AST_Constant; 5099 }); 5100 })(function(node, func) { 5101 node.DEFMETHOD("is_constant", func); 5102 }); 5103 5104 // methods to evaluate a constant expression 5105 (function(def) { 5106 // If the node has been successfully reduced to a constant, 5107 // then its value is returned; otherwise the element itself 5108 // is returned. 5109 // 5110 // They can be distinguished as constant value is never a 5111 // descendant of AST_Node. 5112 // 5113 // When `ignore_side_effects` is `true`, inspect the constant value 5114 // produced without worrying about any side effects caused by said 5115 // expression. 5116 AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) { 5117 if (!compressor.option("evaluate")) return this; 5118 var cached = []; 5119 var val = this._eval(compressor, ignore_side_effects, cached, 1); 5120 cached.forEach(function(node) { 5121 delete node._eval; 5122 }); 5123 if (ignore_side_effects) return val; 5124 if (!val || val instanceof RegExp) return val; 5125 if (typeof val == "function" || typeof val == "object") return this; 5126 return val; 5127 }); 5128 var scan_modified = new TreeWalker(function(node) { 5129 if (node instanceof AST_Assign) modified(node.left); 5130 if (node instanceof AST_ForEnumeration) modified(node.init); 5131 if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression); 5132 }); 5133 function modified(node) { 5134 if (node instanceof AST_DestructuredArray) { 5135 node.elements.forEach(modified); 5136 } else if (node instanceof AST_DestructuredObject) { 5137 node.properties.forEach(function(prop) { 5138 modified(prop.value); 5139 }); 5140 } else if (node instanceof AST_PropAccess) { 5141 modified(node.expression); 5142 } else if (node instanceof AST_SymbolRef) { 5143 node.definition().references.forEach(function(ref) { 5144 delete ref._eval; 5145 }); 5146 } 5147 } 5148 def(AST_Statement, function() { 5149 throw new Error(string_template("Cannot evaluate a statement [{start}]", this)); 5150 }); 5151 def(AST_Accessor, return_this); 5152 def(AST_BigInt, return_this); 5153 def(AST_Class, return_this); 5154 def(AST_Node, return_this); 5155 def(AST_Constant, function() { 5156 return this.value; 5157 }); 5158 def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) { 5159 var lhs = this.left; 5160 if (!ignore_side_effects) { 5161 if (!(lhs instanceof AST_SymbolRef)) return this; 5162 if (!HOP(lhs, "_eval")) { 5163 if (!lhs.fixed) return this; 5164 var def = lhs.definition(); 5165 if (!def.fixed) return this; 5166 if (def.undeclared) return this; 5167 if (def.last_ref !== lhs) return this; 5168 if (def.single_use == "m") return this; 5169 if (this.right.has_side_effects(compressor)) return this; 5170 } 5171 } 5172 var op = this.operator; 5173 var node; 5174 if (!HOP(lhs, "_eval") && lhs instanceof AST_SymbolRef && lhs.fixed && lhs.definition().fixed) { 5175 node = lhs; 5176 } else if (op == "=") { 5177 node = this.right; 5178 } else { 5179 node = make_node(AST_Binary, this, { 5180 operator: op.slice(0, -1), 5181 left: lhs, 5182 right: this.right, 5183 }); 5184 } 5185 lhs.walk(scan_modified); 5186 var value = node._eval(compressor, ignore_side_effects, cached, depth); 5187 if (typeof value == "object") return this; 5188 modified(lhs); 5189 return value; 5190 }); 5191 def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) { 5192 if (!ignore_side_effects) return this; 5193 var exprs = this.expressions; 5194 for (var i = 0, last = exprs.length - 1; i < last; i++) { 5195 exprs[i].walk(scan_modified); 5196 } 5197 var tail = exprs[last]; 5198 var value = tail._eval(compressor, ignore_side_effects, cached, depth); 5199 return value === tail ? this : value; 5200 }); 5201 def(AST_Lambda, function(compressor) { 5202 if (compressor.option("unsafe")) { 5203 var fn = function() {}; 5204 fn.node = this; 5205 fn.toString = function() { 5206 return "function(){}"; 5207 }; 5208 return fn; 5209 } 5210 return this; 5211 }); 5212 def(AST_Array, function(compressor, ignore_side_effects, cached, depth) { 5213 if (compressor.option("unsafe")) { 5214 var elements = []; 5215 for (var i = 0; i < this.elements.length; i++) { 5216 var element = this.elements[i]; 5217 if (element instanceof AST_Hole) return this; 5218 var value = element._eval(compressor, ignore_side_effects, cached, depth); 5219 if (element === value) return this; 5220 elements.push(value); 5221 } 5222 return elements; 5223 } 5224 return this; 5225 }); 5226 def(AST_Object, function(compressor, ignore_side_effects, cached, depth) { 5227 if (compressor.option("unsafe")) { 5228 var val = {}; 5229 for (var i = 0; i < this.properties.length; i++) { 5230 var prop = this.properties[i]; 5231 if (!(prop instanceof AST_ObjectKeyVal)) return this; 5232 var key = prop.key; 5233 if (key instanceof AST_Node) { 5234 key = key._eval(compressor, ignore_side_effects, cached, depth); 5235 if (key === prop.key) return this; 5236 } 5237 switch (key) { 5238 case "__proto__": 5239 case "toString": 5240 case "valueOf": 5241 return this; 5242 } 5243 val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth); 5244 if (val[key] === prop.value) return this; 5245 } 5246 return val; 5247 } 5248 return this; 5249 }); 5250 var non_converting_unary = makePredicate("! typeof void"); 5251 def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) { 5252 var e = this.expression; 5253 var op = this.operator; 5254 // Function would be evaluated to an array and so typeof would 5255 // incorrectly return "object". Hence making is a special case. 5256 if (compressor.option("typeofs") 5257 && op == "typeof" 5258 && (e instanceof AST_Lambda 5259 || e instanceof AST_SymbolRef 5260 && e.fixed_value() instanceof AST_Lambda)) { 5261 return typeof function(){}; 5262 } 5263 var def = e instanceof AST_SymbolRef && e.definition(); 5264 if (!non_converting_unary[op] && !(def && def.fixed)) depth++; 5265 e.walk(scan_modified); 5266 var v = e._eval(compressor, ignore_side_effects, cached, depth); 5267 if (v === e) { 5268 if (ignore_side_effects && op == "void") return; 5269 return this; 5270 } 5271 switch (op) { 5272 case "!": return !v; 5273 case "typeof": 5274 // typeof <RegExp> returns "object" or "function" on different platforms 5275 // so cannot evaluate reliably 5276 if (v instanceof RegExp) return this; 5277 return typeof v; 5278 case "void": return; 5279 case "~": return ~v; 5280 case "-": return -v; 5281 case "+": return +v; 5282 case "++": 5283 case "--": 5284 if (!def) return this; 5285 if (!ignore_side_effects) { 5286 if (def.undeclared) return this; 5287 if (def.last_ref !== e) return this; 5288 } 5289 if (HOP(e, "_eval")) v = +(op[0] + 1) + +v; 5290 modified(e); 5291 return v; 5292 } 5293 return this; 5294 }); 5295 def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) { 5296 var e = this.expression; 5297 if (!(e instanceof AST_SymbolRef)) { 5298 if (!ignore_side_effects) return this; 5299 } else if (!HOP(e, "_eval")) { 5300 if (!e.fixed) return this; 5301 if (!ignore_side_effects) { 5302 var def = e.definition(); 5303 if (!def.fixed) return this; 5304 if (def.undeclared) return this; 5305 if (def.last_ref !== e) return this; 5306 } 5307 } 5308 if (!(e instanceof AST_SymbolRef && e.definition().fixed)) depth++; 5309 e.walk(scan_modified); 5310 var v = e._eval(compressor, ignore_side_effects, cached, depth); 5311 if (v === e) return this; 5312 modified(e); 5313 return +v; 5314 }); 5315 var non_converting_binary = makePredicate("&& || === !=="); 5316 def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) { 5317 if (!non_converting_binary[this.operator]) depth++; 5318 var left = this.left._eval(compressor, ignore_side_effects, cached, depth); 5319 if (left === this.left) return this; 5320 if (this.operator == (left ? "||" : "&&")) return left; 5321 var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object"); 5322 var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth); 5323 if (right === this.right) return this; 5324 var result; 5325 switch (this.operator) { 5326 case "&&" : result = left && right; break; 5327 case "||" : result = left || right; break; 5328 case "??" : 5329 result = left == null ? right : left; 5330 break; 5331 case "|" : result = left | right; break; 5332 case "&" : result = left & right; break; 5333 case "^" : result = left ^ right; break; 5334 case "+" : result = left + right; break; 5335 case "-" : result = left - right; break; 5336 case "*" : result = left * right; break; 5337 case "/" : result = left / right; break; 5338 case "%" : result = left % right; break; 5339 case "<<" : result = left << right; break; 5340 case ">>" : result = left >> right; break; 5341 case ">>>": result = left >>> right; break; 5342 case "==" : result = left == right; break; 5343 case "===": result = left === right; break; 5344 case "!=" : result = left != right; break; 5345 case "!==": result = left !== right; break; 5346 case "<" : result = left < right; break; 5347 case "<=" : result = left <= right; break; 5348 case ">" : result = left > right; break; 5349 case ">=" : result = left >= right; break; 5350 case "**": 5351 result = Math.pow(left, right); 5352 break; 5353 case "in": 5354 if (right && typeof right == "object" && HOP(right, left)) { 5355 result = true; 5356 break; 5357 } 5358 default: 5359 return this; 5360 } 5361 if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result; 5362 if (compressor.option("unsafe_math") 5363 && !ignore_side_effects 5364 && result 5365 && typeof result == "number" 5366 && (this.operator == "+" || this.operator == "-")) { 5367 var digits = Math.max(0, decimals(left), decimals(right)); 5368 // 53-bit significand ---> 15.95 decimal places 5369 if (digits < 16) return +result.toFixed(digits); 5370 } 5371 return result; 5372 5373 function decimals(operand) { 5374 var match = /(\.[0-9]*)?(e[^e]+)?$/.exec(+operand); 5375 return (match[1] || ".").length - 1 - (match[2] || "").slice(1); 5376 } 5377 }); 5378 def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) { 5379 var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth); 5380 if (condition === this.condition) return this; 5381 var node = condition ? this.consequent : this.alternative; 5382 var value = node._eval(compressor, ignore_side_effects, cached, depth); 5383 return value === node ? this : value; 5384 }); 5385 function verify_escaped(ref, depth) { 5386 var escaped = ref.definition().escaped; 5387 switch (escaped.length) { 5388 case 0: 5389 return true; 5390 case 1: 5391 var found = false; 5392 escaped[0].walk(new TreeWalker(function(node) { 5393 if (found) return true; 5394 if (node === ref) return found = true; 5395 if (node instanceof AST_Scope) return true; 5396 })); 5397 return found; 5398 default: 5399 return depth <= escaped.depth; 5400 } 5401 } 5402 def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) { 5403 var fixed = this.fixed_value(); 5404 if (!fixed) return this; 5405 var value; 5406 if (HOP(fixed, "_eval")) { 5407 value = fixed._eval(); 5408 } else { 5409 this._eval = return_this; 5410 value = fixed._eval(compressor, ignore_side_effects, cached, depth); 5411 delete this._eval; 5412 if (value === fixed) return this; 5413 fixed._eval = function() { 5414 return value; 5415 }; 5416 cached.push(fixed); 5417 } 5418 return value && typeof value == "object" && !verify_escaped(this, depth) ? this : value; 5419 }); 5420 var global_objs = { 5421 Array: Array, 5422 Math: Math, 5423 Number: Number, 5424 Object: Object, 5425 String: String, 5426 }; 5427 var static_values = convert_to_predicate({ 5428 Math: [ 5429 "E", 5430 "LN10", 5431 "LN2", 5432 "LOG2E", 5433 "LOG10E", 5434 "PI", 5435 "SQRT1_2", 5436 "SQRT2", 5437 ], 5438 Number: [ 5439 "MAX_VALUE", 5440 "MIN_VALUE", 5441 "NaN", 5442 "NEGATIVE_INFINITY", 5443 "POSITIVE_INFINITY", 5444 ], 5445 }); 5446 var regexp_props = makePredicate("global ignoreCase multiline source"); 5447 def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) { 5448 if (compressor.option("unsafe")) { 5449 var val; 5450 var exp = this.expression; 5451 if (!is_undeclared_ref(exp)) { 5452 val = exp._eval(compressor, ignore_side_effects, cached, depth + 1); 5453 if (val == null || val === exp) return this; 5454 } 5455 var key = this.property; 5456 if (key instanceof AST_Node) { 5457 key = key._eval(compressor, ignore_side_effects, cached, depth); 5458 if (key === this.property) return this; 5459 } 5460 if (val === undefined) { 5461 var static_value = static_values[exp.name]; 5462 if (!static_value || !static_value[key]) return this; 5463 val = global_objs[exp.name]; 5464 } else if (val instanceof RegExp) { 5465 if (!regexp_props[key]) return this; 5466 } else if (typeof val == "object") { 5467 if (!HOP(val, key)) return this; 5468 } else if (typeof val == "function") switch (key) { 5469 case "name": 5470 return val.node.name ? val.node.name.name : ""; 5471 case "length": 5472 return val.node.length(); 5473 default: 5474 return this; 5475 } 5476 return val[key]; 5477 } 5478 return this; 5479 }); 5480 function eval_all(nodes, compressor, ignore_side_effects, cached, depth) { 5481 var values = []; 5482 for (var i = 0; i < nodes.length; i++) { 5483 var node = nodes[i]; 5484 var value = node._eval(compressor, ignore_side_effects, cached, depth); 5485 if (node === value) return; 5486 values.push(value); 5487 } 5488 return values; 5489 } 5490 def(AST_Call, function(compressor, ignore_side_effects, cached, depth) { 5491 var exp = this.expression; 5492 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp; 5493 if (fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function) { 5494 if (fn.evaluating) return this; 5495 if (fn.name && fn.name.definition().recursive_refs > 0) return this; 5496 if (this.is_expr_pure(compressor)) return this; 5497 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth); 5498 if (!all(fn.argnames, function(sym, index) { 5499 if (sym instanceof AST_DefaultValue) { 5500 if (!args) return false; 5501 if (args[index] === undefined) { 5502 var value = sym.value._eval(compressor, ignore_side_effects, cached, depth); 5503 if (value === sym.value) return false; 5504 args[index] = value; 5505 } 5506 sym = sym.name; 5507 } 5508 return !(sym instanceof AST_Destructured); 5509 })) return this; 5510 if (fn.rest instanceof AST_Destructured) return this; 5511 if (!args && !ignore_side_effects) return this; 5512 var stat = fn.first_statement(); 5513 if (!(stat instanceof AST_Return)) { 5514 if (ignore_side_effects) { 5515 fn.walk(scan_modified); 5516 var found = false; 5517 fn.evaluating = true; 5518 walk_body(fn, new TreeWalker(function(node) { 5519 if (found) return true; 5520 if (node instanceof AST_Return) { 5521 if (node.value && node.value._eval(compressor, true, cached, depth) !== undefined) { 5522 found = true; 5523 } 5524 return true; 5525 } 5526 if (node instanceof AST_Scope && node !== fn) return true; 5527 })); 5528 fn.evaluating = false; 5529 if (!found) return; 5530 } 5531 return this; 5532 } 5533 var val = stat.value; 5534 if (!val) return; 5535 var cached_args = []; 5536 if (!args || all(fn.argnames, function(sym, i) { 5537 return assign(sym, args[i]); 5538 }) && !(fn.rest && !assign(fn.rest, args.slice(fn.argnames.length))) || ignore_side_effects) { 5539 if (ignore_side_effects) fn.argnames.forEach(function(sym) { 5540 if (sym instanceof AST_DefaultValue) sym.value.walk(scan_modified); 5541 }); 5542 fn.evaluating = true; 5543 val = val._eval(compressor, ignore_side_effects, cached, depth); 5544 fn.evaluating = false; 5545 } 5546 cached_args.forEach(function(node) { 5547 delete node._eval; 5548 }); 5549 return val === stat.value ? this : val; 5550 } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) { 5551 var key = exp.property; 5552 if (key instanceof AST_Node) { 5553 key = key._eval(compressor, ignore_side_effects, cached, depth); 5554 if (key === exp.property) return this; 5555 } 5556 var val; 5557 var e = exp.expression; 5558 if (is_undeclared_ref(e)) { 5559 var static_fn = static_fns[e.name]; 5560 if (!static_fn || !static_fn[key]) return this; 5561 val = global_objs[e.name]; 5562 } else { 5563 val = e._eval(compressor, ignore_side_effects, cached, depth + 1); 5564 if (val == null || val === e) return this; 5565 var native_fn = native_fns[val.constructor.name]; 5566 if (!native_fn || !native_fn[key]) return this; 5567 if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this; 5568 } 5569 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth); 5570 if (!args) return this; 5571 if (key == "replace" && typeof args[1] == "function") return this; 5572 try { 5573 return val[key].apply(val, args); 5574 } catch (ex) { 5575 AST_Node.warn("Error evaluating {this} [{start}]", this); 5576 } finally { 5577 if (val instanceof RegExp) val.lastIndex = 0; 5578 } 5579 } 5580 return this; 5581 5582 function assign(sym, arg) { 5583 if (sym instanceof AST_DefaultValue) sym = sym.name; 5584 var def = sym.definition(); 5585 if (def.orig[def.orig.length - 1] !== sym) return false; 5586 var value = arg; 5587 def.references.forEach(function(node) { 5588 node._eval = function() { 5589 return value; 5590 }; 5591 cached_args.push(node); 5592 }); 5593 return true; 5594 } 5595 }); 5596 def(AST_New, return_this); 5597 def(AST_Template, function(compressor, ignore_side_effects, cached, depth) { 5598 if (!compressor.option("templates")) return this; 5599 if (this.tag) { 5600 if (!is_raw_tag(compressor, this.tag)) return this; 5601 decode = function(str) { 5602 return str; 5603 }; 5604 } 5605 var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth); 5606 if (!exprs) return this; 5607 var malformed = false; 5608 var ret = decode(this.strings[0]); 5609 for (var i = 0; i < exprs.length; i++) { 5610 ret += exprs[i] + decode(this.strings[i + 1]); 5611 } 5612 if (!malformed) return ret; 5613 this._eval = return_this; 5614 return this; 5615 5616 function decode(str) { 5617 str = decode_template(str); 5618 if (typeof str != "string") malformed = true; 5619 return str; 5620 } 5621 }); 5622 })(function(node, func) { 5623 node.DEFMETHOD("_eval", func); 5624 }); 5625 5626 // method to negate an expression 5627 (function(def) { 5628 function basic_negation(exp) { 5629 return make_node(AST_UnaryPrefix, exp, { 5630 operator: "!", 5631 expression: exp, 5632 }); 5633 } 5634 function best(orig, alt, first_in_statement) { 5635 var negated = basic_negation(orig); 5636 if (first_in_statement) return best_of_expression(negated, make_node(AST_SimpleStatement, alt, { 5637 body: alt, 5638 })) === negated ? negated : alt; 5639 return best_of_expression(negated, alt); 5640 } 5641 def(AST_Node, function() { 5642 return basic_negation(this); 5643 }); 5644 def(AST_Statement, function() { 5645 throw new Error("Cannot negate a statement"); 5646 }); 5647 def(AST_Binary, function(compressor, first_in_statement) { 5648 var self = this.clone(), op = this.operator; 5649 if (compressor.option("unsafe_comps")) { 5650 switch (op) { 5651 case "<=" : self.operator = ">" ; return self; 5652 case "<" : self.operator = ">=" ; return self; 5653 case ">=" : self.operator = "<" ; return self; 5654 case ">" : self.operator = "<=" ; return self; 5655 } 5656 } 5657 switch (op) { 5658 case "==" : self.operator = "!="; return self; 5659 case "!=" : self.operator = "=="; return self; 5660 case "===": self.operator = "!=="; return self; 5661 case "!==": self.operator = "==="; return self; 5662 case "&&": 5663 self.operator = "||"; 5664 self.left = self.left.negate(compressor, first_in_statement); 5665 self.right = self.right.negate(compressor); 5666 return best(this, self, first_in_statement); 5667 case "||": 5668 self.operator = "&&"; 5669 self.left = self.left.negate(compressor, first_in_statement); 5670 self.right = self.right.negate(compressor); 5671 return best(this, self, first_in_statement); 5672 } 5673 return basic_negation(this); 5674 }); 5675 def(AST_ClassExpression, function() { 5676 return basic_negation(this); 5677 }); 5678 def(AST_Conditional, function(compressor, first_in_statement) { 5679 var self = this.clone(); 5680 self.consequent = self.consequent.negate(compressor); 5681 self.alternative = self.alternative.negate(compressor); 5682 return best(this, self, first_in_statement); 5683 }); 5684 def(AST_LambdaExpression, function() { 5685 return basic_negation(this); 5686 }); 5687 def(AST_Sequence, function(compressor) { 5688 var expressions = this.expressions.slice(); 5689 expressions.push(expressions.pop().negate(compressor)); 5690 return make_sequence(this, expressions); 5691 }); 5692 def(AST_UnaryPrefix, function() { 5693 if (this.operator == "!") 5694 return this.expression; 5695 return basic_negation(this); 5696 }); 5697 })(function(node, func) { 5698 node.DEFMETHOD("negate", function(compressor, first_in_statement) { 5699 return func.call(this, compressor, first_in_statement); 5700 }); 5701 }); 5702 5703 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"); 5704 var global_pure_constructors = makePredicate("Map Set WeakMap WeakSet"); 5705 AST_Call.DEFMETHOD("is_expr_pure", function(compressor) { 5706 if (compressor.option("unsafe")) { 5707 var expr = this.expression; 5708 if (is_undeclared_ref(expr)) { 5709 if (global_pure_fns[expr.name]) return true; 5710 if (this instanceof AST_New && global_pure_constructors[expr.name]) return true; 5711 } 5712 if (is_static_fn(expr)) return true; 5713 } 5714 return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this); 5715 }); 5716 AST_Template.DEFMETHOD("is_expr_pure", function(compressor) { 5717 var tag = this.tag; 5718 if (!tag) return true; 5719 if (compressor.option("unsafe")) { 5720 if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true; 5721 if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) { 5722 var static_fn = static_fns[tag.expression.name]; 5723 return static_fn && (static_fn[tag.property] 5724 || tag.expression.name == "Math" && tag.property == "random"); 5725 } 5726 } 5727 return !compressor.pure_funcs(this); 5728 }); 5729 AST_Node.DEFMETHOD("is_call_pure", return_false); 5730 AST_Call.DEFMETHOD("is_call_pure", function(compressor) { 5731 if (!compressor.option("unsafe")) return false; 5732 var dot = this.expression; 5733 if (!(dot instanceof AST_Dot)) return false; 5734 var exp = dot.expression; 5735 var map; 5736 var prop = dot.property; 5737 if (exp instanceof AST_Array) { 5738 map = native_fns.Array; 5739 } else if (exp.is_boolean(compressor)) { 5740 map = native_fns.Boolean; 5741 } else if (exp.is_number(compressor)) { 5742 map = native_fns.Number; 5743 } else if (exp instanceof AST_RegExp) { 5744 map = native_fns.RegExp; 5745 } else if (exp.is_string(compressor)) { 5746 map = native_fns.String; 5747 if (prop == "replace") { 5748 var arg = this.args[1]; 5749 if (arg && !arg.is_string(compressor)) return false; 5750 } 5751 } else if (!dot.may_throw_on_access(compressor)) { 5752 map = native_fns.Object; 5753 } 5754 return map && map[prop]; 5755 }); 5756 5757 // determine if object spread syntax may cause runtime exception 5758 (function(def) { 5759 def(AST_Node, return_false); 5760 def(AST_Array, return_true); 5761 def(AST_Assign, function() { 5762 switch (this.operator) { 5763 case "=": 5764 return this.right.safe_to_spread(); 5765 case "&&=": 5766 case "||=": 5767 case "??=": 5768 return this.left.safe_to_spread() && this.right.safe_to_spread(); 5769 } 5770 return true; 5771 }); 5772 def(AST_Binary, function() { 5773 return !lazy_op[this.operator] || this.left.safe_to_spread() && this.right.safe_to_spread(); 5774 }); 5775 def(AST_Constant, return_true); 5776 def(AST_Lambda, return_true); 5777 def(AST_Object, function() { 5778 return all(this.properties, function(prop) { 5779 return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread); 5780 }); 5781 }); 5782 def(AST_Sequence, function() { 5783 return this.tail_node().safe_to_spread(); 5784 }); 5785 def(AST_SymbolRef, function() { 5786 var fixed = this.fixed_value(); 5787 return fixed && fixed.safe_to_spread(); 5788 }); 5789 def(AST_Unary, return_true); 5790 })(function(node, func) { 5791 node.DEFMETHOD("safe_to_spread", func); 5792 }); 5793 5794 // determine if expression has side effects 5795 (function(def) { 5796 function any(list, compressor, spread) { 5797 return !all(list, spread ? function(node) { 5798 return node instanceof AST_Spread ? !spread(node, compressor) : !node.has_side_effects(compressor); 5799 } : function(node) { 5800 return !node.has_side_effects(compressor); 5801 }); 5802 } 5803 function array_spread(node, compressor) { 5804 var exp = node.expression; 5805 return !exp.is_string(compressor) || exp.has_side_effects(compressor); 5806 } 5807 def(AST_Node, return_true); 5808 def(AST_Array, function(compressor) { 5809 return any(this.elements, compressor, array_spread); 5810 }); 5811 def(AST_Assign, function(compressor) { 5812 var lhs = this.left; 5813 if (!(lhs instanceof AST_PropAccess)) return true; 5814 var node = lhs.expression; 5815 return !(node instanceof AST_ObjectIdentity) 5816 || !node.scope.resolve().new 5817 || lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor) 5818 || this.right.has_side_effects(compressor); 5819 }); 5820 def(AST_Binary, function(compressor) { 5821 return this.left.has_side_effects(compressor) 5822 || this.right.has_side_effects(compressor) 5823 || !can_drop_op(this.operator, this.right, compressor); 5824 }); 5825 def(AST_Block, function(compressor) { 5826 return any(this.body, compressor); 5827 }); 5828 def(AST_Call, function(compressor) { 5829 if (!this.is_expr_pure(compressor) 5830 && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) { 5831 return true; 5832 } 5833 return any(this.args, compressor, array_spread); 5834 }); 5835 def(AST_Case, function(compressor) { 5836 return this.expression.has_side_effects(compressor) 5837 || any(this.body, compressor); 5838 }); 5839 def(AST_Class, function(compressor) { 5840 var base = this.extends; 5841 if (base) { 5842 if (base instanceof AST_SymbolRef) base = base.fixed_value(); 5843 if (!safe_for_extends(base)) return true; 5844 } 5845 return any(this.properties, compressor); 5846 }); 5847 def(AST_ClassProperty, function(compressor) { 5848 return this.key instanceof AST_Node && this.key.has_side_effects(compressor) 5849 || this.static && this.value && this.value.has_side_effects(compressor); 5850 }); 5851 def(AST_Conditional, function(compressor) { 5852 return this.condition.has_side_effects(compressor) 5853 || this.consequent.has_side_effects(compressor) 5854 || this.alternative.has_side_effects(compressor); 5855 }); 5856 def(AST_Constant, return_false); 5857 def(AST_Definitions, function(compressor) { 5858 return any(this.definitions, compressor); 5859 }); 5860 def(AST_DestructuredArray, function(compressor) { 5861 return any(this.elements, compressor); 5862 }); 5863 def(AST_DestructuredKeyVal, function(compressor) { 5864 return this.key instanceof AST_Node && this.key.has_side_effects(compressor) 5865 || this.value.has_side_effects(compressor); 5866 }); 5867 def(AST_DestructuredObject, function(compressor) { 5868 return any(this.properties, compressor); 5869 }); 5870 def(AST_Dot, function(compressor) { 5871 return this.expression.may_throw_on_access(compressor) 5872 || this.expression.has_side_effects(compressor); 5873 }); 5874 def(AST_EmptyStatement, return_false); 5875 def(AST_If, function(compressor) { 5876 return this.condition.has_side_effects(compressor) 5877 || this.body && this.body.has_side_effects(compressor) 5878 || this.alternative && this.alternative.has_side_effects(compressor); 5879 }); 5880 def(AST_LabeledStatement, function(compressor) { 5881 return this.body.has_side_effects(compressor); 5882 }); 5883 def(AST_Lambda, return_false); 5884 def(AST_Object, function(compressor) { 5885 return any(this.properties, compressor, function(node, compressor) { 5886 var exp = node.expression; 5887 return !exp.safe_to_spread() || exp.has_side_effects(compressor); 5888 }); 5889 }); 5890 def(AST_ObjectIdentity, return_false); 5891 def(AST_ObjectProperty, function(compressor) { 5892 return this.key instanceof AST_Node && this.key.has_side_effects(compressor) 5893 || this.value.has_side_effects(compressor); 5894 }); 5895 def(AST_Sequence, function(compressor) { 5896 return any(this.expressions, compressor); 5897 }); 5898 def(AST_SimpleStatement, function(compressor) { 5899 return this.body.has_side_effects(compressor); 5900 }); 5901 def(AST_Sub, function(compressor) { 5902 return this.expression.may_throw_on_access(compressor) 5903 || this.expression.has_side_effects(compressor) 5904 || this.property.has_side_effects(compressor); 5905 }); 5906 def(AST_Switch, function(compressor) { 5907 return this.expression.has_side_effects(compressor) 5908 || any(this.body, compressor); 5909 }); 5910 def(AST_SymbolDeclaration, return_false); 5911 def(AST_SymbolRef, function(compressor) { 5912 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor); 5913 }); 5914 def(AST_Template, function(compressor) { 5915 return !this.is_expr_pure(compressor) || any(this.expressions, compressor); 5916 }); 5917 def(AST_Try, function(compressor) { 5918 return any(this.body, compressor) 5919 || this.bcatch && this.bcatch.has_side_effects(compressor) 5920 || this.bfinally && this.bfinally.has_side_effects(compressor); 5921 }); 5922 def(AST_Unary, function(compressor) { 5923 return unary_side_effects[this.operator] 5924 || this.expression.has_side_effects(compressor); 5925 }); 5926 def(AST_VarDef, function() { 5927 return this.value; 5928 }); 5929 })(function(node, func) { 5930 node.DEFMETHOD("has_side_effects", func); 5931 }); 5932 5933 // determine if expression may throw 5934 (function(def) { 5935 def(AST_Node, return_true); 5936 5937 def(AST_Constant, return_false); 5938 def(AST_EmptyStatement, return_false); 5939 def(AST_Lambda, return_false); 5940 def(AST_ObjectIdentity, return_false); 5941 def(AST_SymbolDeclaration, return_false); 5942 5943 function any(list, compressor) { 5944 for (var i = list.length; --i >= 0;) 5945 if (list[i].may_throw(compressor)) 5946 return true; 5947 return false; 5948 } 5949 5950 function call_may_throw(exp, compressor) { 5951 if (exp.may_throw(compressor)) return true; 5952 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value(); 5953 if (!(exp instanceof AST_Lambda)) return true; 5954 if (any(exp.argnames, compressor)) return true; 5955 if (any(exp.body, compressor)) return true; 5956 return is_arrow(exp) && exp.value && exp.value.may_throw(compressor); 5957 } 5958 5959 def(AST_Array, function(compressor) { 5960 return any(this.elements, compressor); 5961 }); 5962 def(AST_Assign, function(compressor) { 5963 if (this.right.may_throw(compressor)) return true; 5964 if (!compressor.has_directive("use strict") 5965 && this.operator == "=" 5966 && this.left instanceof AST_SymbolRef) { 5967 return false; 5968 } 5969 return this.left.may_throw(compressor); 5970 }); 5971 def(AST_Await, function(compressor) { 5972 return this.expression.may_throw(compressor); 5973 }); 5974 def(AST_Binary, function(compressor) { 5975 return this.left.may_throw(compressor) 5976 || this.right.may_throw(compressor) 5977 || !can_drop_op(this.operator, this.right, compressor); 5978 }); 5979 def(AST_Block, function(compressor) { 5980 return any(this.body, compressor); 5981 }); 5982 def(AST_Call, function(compressor) { 5983 if (any(this.args, compressor)) return true; 5984 if (this.is_expr_pure(compressor)) return false; 5985 this.may_throw = return_true; 5986 var ret = call_may_throw(this.expression, compressor); 5987 delete this.may_throw; 5988 return ret; 5989 }); 5990 def(AST_Case, function(compressor) { 5991 return this.expression.may_throw(compressor) 5992 || any(this.body, compressor); 5993 }); 5994 def(AST_Conditional, function(compressor) { 5995 return this.condition.may_throw(compressor) 5996 || this.consequent.may_throw(compressor) 5997 || this.alternative.may_throw(compressor); 5998 }); 5999 def(AST_DefaultValue, function(compressor) { 6000 return this.name.may_throw(compressor) 6001 || this.value && this.value.may_throw(compressor); 6002 }); 6003 def(AST_Definitions, function(compressor) { 6004 return any(this.definitions, compressor); 6005 }); 6006 def(AST_Dot, function(compressor) { 6007 return !this.optional && this.expression.may_throw_on_access(compressor) 6008 || this.expression.may_throw(compressor); 6009 }); 6010 def(AST_ForEnumeration, function(compressor) { 6011 if (this.init.may_throw(compressor)) return true; 6012 var obj = this.object; 6013 if (obj.may_throw(compressor)) return true; 6014 obj = obj.tail_node(); 6015 if (!(obj instanceof AST_Array || obj.is_string(compressor))) return true; 6016 return this.body.may_throw(compressor); 6017 }); 6018 def(AST_If, function(compressor) { 6019 return this.condition.may_throw(compressor) 6020 || this.body && this.body.may_throw(compressor) 6021 || this.alternative && this.alternative.may_throw(compressor); 6022 }); 6023 def(AST_LabeledStatement, function(compressor) { 6024 return this.body.may_throw(compressor); 6025 }); 6026 def(AST_Object, function(compressor) { 6027 return any(this.properties, compressor); 6028 }); 6029 def(AST_ObjectProperty, function(compressor) { 6030 return this.value.may_throw(compressor) 6031 || this.key instanceof AST_Node && this.key.may_throw(compressor); 6032 }); 6033 def(AST_Return, function(compressor) { 6034 return this.value && this.value.may_throw(compressor); 6035 }); 6036 def(AST_Sequence, function(compressor) { 6037 return any(this.expressions, compressor); 6038 }); 6039 def(AST_SimpleStatement, function(compressor) { 6040 return this.body.may_throw(compressor); 6041 }); 6042 def(AST_Sub, function(compressor) { 6043 return !this.optional && this.expression.may_throw_on_access(compressor) 6044 || this.expression.may_throw(compressor) 6045 || this.property.may_throw(compressor); 6046 }); 6047 def(AST_Switch, function(compressor) { 6048 return this.expression.may_throw(compressor) 6049 || any(this.body, compressor); 6050 }); 6051 def(AST_SymbolRef, function(compressor) { 6052 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor); 6053 }); 6054 def(AST_Template, function(compressor) { 6055 if (any(this.expressions, compressor)) return true; 6056 if (this.is_expr_pure(compressor)) return false; 6057 if (!this.tag) return false; 6058 this.may_throw = return_true; 6059 var ret = call_may_throw(this.tag, compressor); 6060 delete this.may_throw; 6061 return ret; 6062 }); 6063 def(AST_Try, function(compressor) { 6064 return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor)) 6065 || this.bfinally && this.bfinally.may_throw(compressor); 6066 }); 6067 def(AST_Unary, function(compressor) { 6068 return this.expression.may_throw(compressor) 6069 && !(this.operator == "typeof" && this.expression instanceof AST_SymbolRef); 6070 }); 6071 def(AST_VarDef, function(compressor) { 6072 return this.name.may_throw(compressor) 6073 || this.value && this.value.may_throw(compressor); 6074 }); 6075 })(function(node, func) { 6076 node.DEFMETHOD("may_throw", func); 6077 }); 6078 6079 // determine if expression is constant 6080 (function(def) { 6081 function all_constant(list, scope) { 6082 for (var i = list.length; --i >= 0;) 6083 if (!list[i].is_constant_expression(scope)) 6084 return false; 6085 return true; 6086 } 6087 def(AST_Node, return_false); 6088 def(AST_Array, function(scope) { 6089 return all_constant(this.elements, scope); 6090 }); 6091 def(AST_Binary, function(scope) { 6092 return this.left.is_constant_expression(scope) 6093 && this.right.is_constant_expression(scope) 6094 && can_drop_op(this.operator, this.right); 6095 }); 6096 def(AST_Class, function(scope) { 6097 var base = this.extends; 6098 if (base && !safe_for_extends(base)) return false; 6099 return all_constant(this.properties, scope); 6100 }); 6101 def(AST_ClassProperty, function(scope) { 6102 return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope)); 6103 }); 6104 def(AST_Constant, return_true); 6105 def(AST_Lambda, function(scope) { 6106 var self = this; 6107 var result = true; 6108 var scopes = []; 6109 self.walk(new TreeWalker(function(node, descend) { 6110 if (!result) return true; 6111 if (node instanceof AST_BlockScope) { 6112 if (node === self) return; 6113 scopes.push(node); 6114 descend(); 6115 scopes.pop(); 6116 return true; 6117 } 6118 if (node instanceof AST_SymbolRef) { 6119 if (self.inlined || node.redef || node.in_arg) { 6120 result = false; 6121 return true; 6122 } 6123 if (self.variables.has(node.name)) return true; 6124 var def = node.definition(); 6125 if (member(def.scope, scopes)) return true; 6126 if (scope && !def.redefined()) { 6127 var scope_def = scope.find_variable(node.name); 6128 if (scope_def ? scope_def === def : def.undeclared) { 6129 result = "f"; 6130 return true; 6131 } 6132 } 6133 result = false; 6134 return true; 6135 } 6136 if (node instanceof AST_ObjectIdentity) { 6137 if (is_arrow(self) && all(scopes, function(s) { 6138 return !(s instanceof AST_Scope) || is_arrow(s); 6139 })) result = false; 6140 return true; 6141 } 6142 })); 6143 return result; 6144 }); 6145 def(AST_Object, function(scope) { 6146 return all_constant(this.properties, scope); 6147 }); 6148 def(AST_ObjectProperty, function(scope) { 6149 return typeof this.key == "string" && this.value.is_constant_expression(scope); 6150 }); 6151 def(AST_Unary, function(scope) { 6152 return this.expression.is_constant_expression(scope); 6153 }); 6154 })(function(node, func) { 6155 node.DEFMETHOD("is_constant_expression", func); 6156 }); 6157 6158 // tell me if a statement aborts 6159 function aborts(thing) { 6160 return thing && thing.aborts(); 6161 } 6162 (function(def) { 6163 def(AST_Statement, return_null); 6164 def(AST_Jump, return_this); 6165 function block_aborts() { 6166 var n = this.body.length; 6167 return n > 0 && aborts(this.body[n - 1]); 6168 } 6169 def(AST_BlockStatement, block_aborts); 6170 def(AST_SwitchBranch, block_aborts); 6171 def(AST_If, function() { 6172 return this.alternative && aborts(this.body) && aborts(this.alternative) && this; 6173 }); 6174 })(function(node, func) { 6175 node.DEFMETHOD("aborts", func); 6176 }); 6177 6178 /* -----[ optimizers ]----- */ 6179 6180 var directives = makePredicate(["use asm", "use strict"]); 6181 OPT(AST_Directive, function(self, compressor) { 6182 if (compressor.option("directives") 6183 && (!directives[self.value] || compressor.has_directive(self.value) !== self)) { 6184 return make_node(AST_EmptyStatement, self); 6185 } 6186 return self; 6187 }); 6188 6189 OPT(AST_Debugger, function(self, compressor) { 6190 if (compressor.option("drop_debugger")) 6191 return make_node(AST_EmptyStatement, self); 6192 return self; 6193 }); 6194 6195 OPT(AST_LabeledStatement, function(self, compressor) { 6196 if (self.body instanceof AST_If || self.body instanceof AST_Break) { 6197 var body = tighten_body([ self.body ], compressor); 6198 switch (body.length) { 6199 case 0: 6200 self.body = make_node(AST_EmptyStatement, self); 6201 break; 6202 case 1: 6203 self.body = body[0]; 6204 break; 6205 default: 6206 self.body = make_node(AST_BlockStatement, self, { body: body }); 6207 break; 6208 } 6209 } 6210 return compressor.option("unused") && self.label.references.length == 0 ? self.body : self; 6211 }); 6212 6213 OPT(AST_LoopControl, function(self, compressor) { 6214 if (!compressor.option("dead_code")) return self; 6215 var label = self.label; 6216 if (label) { 6217 var lct = compressor.loopcontrol_target(self); 6218 self.label = null; 6219 if (compressor.loopcontrol_target(self) === lct) { 6220 remove(label.thedef.references, self); 6221 } else { 6222 self.label = label; 6223 } 6224 } 6225 return self; 6226 }); 6227 6228 OPT(AST_Block, function(self, compressor) { 6229 self.body = tighten_body(self.body, compressor); 6230 return self; 6231 }); 6232 6233 function trim_block(node, parent, in_list) { 6234 switch (node.body.length) { 6235 case 0: 6236 return in_list ? List.skip : make_node(AST_EmptyStatement, node); 6237 case 1: 6238 var stat = node.body[0]; 6239 if (!safe_to_trim(stat)) return node; 6240 if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node; 6241 return stat; 6242 } 6243 return node; 6244 } 6245 6246 OPT(AST_BlockStatement, function(self, compressor) { 6247 self.body = tighten_body(self.body, compressor); 6248 return trim_block(self, compressor.parent()); 6249 }); 6250 6251 function drop_rest_farg(fn, compressor) { 6252 if (!compressor.option("rests")) return; 6253 if (fn.uses_arguments) return; 6254 if (!(fn.rest instanceof AST_DestructuredArray)) return; 6255 if (!compressor.drop_fargs(fn, compressor.parent())) return; 6256 fn.argnames = fn.argnames.concat(fn.rest.elements); 6257 fn.rest = fn.rest.rest; 6258 } 6259 6260 OPT(AST_Lambda, function(self, compressor) { 6261 drop_rest_farg(self, compressor); 6262 self.body = tighten_body(self.body, compressor); 6263 return self; 6264 }); 6265 6266 function opt_arrow(self, compressor) { 6267 if (!compressor.option("arrows")) return self; 6268 drop_rest_farg(self, compressor); 6269 if (self.value) self.body = [ self.first_statement() ]; 6270 var body = tighten_body(self.body, compressor); 6271 switch (body.length) { 6272 case 1: 6273 var stat = body[0]; 6274 if (stat instanceof AST_Return) { 6275 self.body.length = 0; 6276 self.value = stat.value; 6277 break; 6278 } 6279 default: 6280 self.body = body; 6281 self.value = null; 6282 break; 6283 } 6284 return self; 6285 } 6286 OPT(AST_Arrow, opt_arrow); 6287 OPT(AST_AsyncArrow, opt_arrow); 6288 6289 OPT(AST_Function, function(self, compressor) { 6290 drop_rest_farg(self, compressor); 6291 self.body = tighten_body(self.body, compressor); 6292 var parent = compressor.parent(); 6293 if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) { 6294 var stat = self.body[i]; 6295 if (stat instanceof AST_Directive) continue; 6296 if (stat instanceof AST_Return) { 6297 if (i != self.body.length - 1) break; 6298 var call = stat.value; 6299 if (!call || call.TYPE != "Call") break; 6300 if (call.is_expr_pure(compressor)) break; 6301 var exp = call.expression, fn; 6302 if (!(exp instanceof AST_SymbolRef)) { 6303 fn = exp; 6304 } else if (self.name && self.name.definition() === exp.definition()) { 6305 break; 6306 } else { 6307 fn = exp.fixed_value(); 6308 } 6309 if (!(fn instanceof AST_Defun || fn instanceof AST_Function)) break; 6310 if (fn.rest) break; 6311 if (fn.uses_arguments) break; 6312 if (fn === exp) { 6313 if (fn.parent_scope !== self) break; 6314 if (!all(fn.enclosed, function(def) { 6315 return def.scope !== self; 6316 })) break; 6317 } 6318 if ((fn !== exp || fn.name) 6319 && (parent instanceof AST_ClassMethod || parent instanceof AST_ObjectMethod) 6320 && parent.value === compressor.self()) break; 6321 if (fn.contains_this()) break; 6322 var len = fn.argnames.length; 6323 if (len > 0 && compressor.option("inline") < 2) break; 6324 if (len > self.argnames.length) break; 6325 if (!all(self.argnames, function(argname) { 6326 return argname instanceof AST_SymbolFunarg; 6327 })) break; 6328 if (!all(call.args, function(arg) { 6329 return !(arg instanceof AST_Spread); 6330 })) break; 6331 for (var j = 0; j < len; j++) { 6332 var arg = call.args[j]; 6333 if (!(arg instanceof AST_SymbolRef)) break; 6334 if (arg.definition() !== self.argnames[j].definition()) break; 6335 } 6336 if (j < len) break; 6337 for (; j < call.args.length; j++) { 6338 if (call.args[j].has_side_effects(compressor)) break; 6339 } 6340 if (j < call.args.length) break; 6341 if (len < self.argnames.length && !compressor.drop_fargs(self, parent)) { 6342 if (!compressor.drop_fargs(fn, call)) break; 6343 do { 6344 fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len)); 6345 } while (++len < self.argnames.length); 6346 } 6347 return exp; 6348 } 6349 break; 6350 } 6351 return self; 6352 }); 6353 6354 var NO_MERGE = makePredicate("arguments await yield"); 6355 AST_Scope.DEFMETHOD("merge_variables", function(compressor) { 6356 if (!compressor.option("merge_vars")) return; 6357 var in_arg = [], in_try, root, segment = {}, self = this; 6358 var first = [], last = [], index = 0; 6359 var declarations = new Dictionary(); 6360 var references = Object.create(null); 6361 var prev = Object.create(null); 6362 var tw = new TreeWalker(function(node, descend) { 6363 if (node instanceof AST_Assign) { 6364 var lhs = node.left; 6365 var rhs = node.right; 6366 if (lhs instanceof AST_Destructured) { 6367 rhs.walk(tw); 6368 walk_destructured(AST_SymbolRef, mark, lhs); 6369 return true; 6370 } 6371 if (lazy_op[node.operator.slice(0, -1)]) { 6372 lhs.walk(tw); 6373 push(); 6374 rhs.walk(tw); 6375 if (lhs instanceof AST_SymbolRef) mark(lhs); 6376 pop(); 6377 return true; 6378 } 6379 if (lhs instanceof AST_SymbolRef) { 6380 if (node.operator != "=") mark(lhs, true); 6381 rhs.walk(tw); 6382 mark(lhs); 6383 return true; 6384 } 6385 return; 6386 } 6387 if (node instanceof AST_Binary) { 6388 if (!lazy_op[node.operator]) return; 6389 walk_cond(node); 6390 return true; 6391 } 6392 if (node instanceof AST_Break) { 6393 var target = tw.loopcontrol_target(node); 6394 if (!(target instanceof AST_IterationStatement)) insert(target); 6395 return true; 6396 } 6397 if (node instanceof AST_Call) { 6398 var exp = node.expression; 6399 if (exp instanceof AST_LambdaExpression) { 6400 node.args.forEach(function(arg) { 6401 arg.walk(tw); 6402 }); 6403 exp.walk(tw); 6404 } else { 6405 descend(); 6406 mark_expression(exp); 6407 } 6408 return true; 6409 } 6410 if (node instanceof AST_Class) { 6411 if (node.name) node.name.walk(tw); 6412 if (node.extends) node.extends.walk(tw); 6413 node.properties.filter(function(prop) { 6414 if (prop.key instanceof AST_Node) prop.key.walk(tw); 6415 return prop.value; 6416 }).forEach(function(prop) { 6417 if (prop.static) { 6418 prop.value.walk(tw); 6419 } else { 6420 push(); 6421 segment.block = node; 6422 prop.value.walk(tw); 6423 pop(); 6424 } 6425 }); 6426 return true; 6427 } 6428 if (node instanceof AST_Conditional) { 6429 walk_cond(node.condition, node.consequent, node.alternative); 6430 return true; 6431 } 6432 if (node instanceof AST_Continue) { 6433 var target = tw.loopcontrol_target(node); 6434 if (target instanceof AST_Do) insert(target); 6435 return true; 6436 } 6437 if (node instanceof AST_Do) { 6438 push(); 6439 segment.block = node; 6440 segment.loop = true; 6441 var save = segment; 6442 node.body.walk(tw); 6443 if (segment.inserted === node) segment = save; 6444 node.condition.walk(tw); 6445 pop(); 6446 return true; 6447 } 6448 if (node instanceof AST_For) { 6449 if (node.init) node.init.walk(tw); 6450 push(); 6451 segment.block = node; 6452 segment.loop = true; 6453 if (node.condition) node.condition.walk(tw); 6454 node.body.walk(tw); 6455 if (node.step) node.step.walk(tw); 6456 pop(); 6457 return true; 6458 } 6459 if (node instanceof AST_ForEnumeration) { 6460 node.object.walk(tw); 6461 push(); 6462 segment.block = node; 6463 segment.loop = true; 6464 node.init.walk(tw); 6465 node.body.walk(tw); 6466 pop(); 6467 return true; 6468 } 6469 if (node instanceof AST_If) { 6470 walk_cond(node.condition, node.body, node.alternative); 6471 return true; 6472 } 6473 if (node instanceof AST_LabeledStatement) { 6474 push(); 6475 segment.block = node; 6476 var save = segment; 6477 node.body.walk(tw); 6478 if (segment.inserted === node) segment = save; 6479 pop(); 6480 return true; 6481 } 6482 if (node instanceof AST_Scope) { 6483 push(); 6484 segment.block = node; 6485 if (node === self) root = segment; 6486 if (node instanceof AST_Lambda) { 6487 if (node.name) references[node.name.definition().id] = false; 6488 var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) { 6489 references[node.definition().id] = false; 6490 } : function(node) { 6491 mark(node); 6492 }; 6493 in_arg.push(node); 6494 node.argnames.forEach(function(argname) { 6495 walk_destructured(AST_SymbolFunarg, marker, argname); 6496 }); 6497 if (node.rest) walk_destructured(AST_SymbolFunarg, marker, node.rest); 6498 in_arg.pop(); 6499 } 6500 walk_lambda(node, tw); 6501 pop(); 6502 return true; 6503 } 6504 if (node instanceof AST_Sub) { 6505 var exp = node.expression; 6506 if (node.optional) { 6507 exp.walk(tw); 6508 push(); 6509 node.property.walk(tw); 6510 pop(); 6511 } else { 6512 descend(); 6513 } 6514 mark_expression(exp); 6515 return true; 6516 } 6517 if (node instanceof AST_Switch) { 6518 node.expression.walk(tw); 6519 var save = segment; 6520 node.body.forEach(function(branch) { 6521 if (branch instanceof AST_Default) return; 6522 branch.expression.walk(tw); 6523 if (save === segment) push(); 6524 }); 6525 segment = save; 6526 node.body.forEach(function(branch) { 6527 push(); 6528 segment.block = node; 6529 var save = segment; 6530 walk_body(branch, tw); 6531 if (segment.inserted === node) segment = save; 6532 pop(); 6533 }); 6534 return true; 6535 } 6536 if (node instanceof AST_SymbolConst || node instanceof AST_SymbolLet) { 6537 references[node.definition().id] = false; 6538 return true; 6539 } 6540 if (node instanceof AST_SymbolRef) { 6541 mark(node, true); 6542 return true; 6543 } 6544 if (node instanceof AST_Try) { 6545 var save_try = in_try; 6546 in_try = node; 6547 walk_body(node, tw); 6548 if (node.bcatch) { 6549 if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) { 6550 if (node instanceof AST_SymbolCatch) { 6551 var def = node.definition(); 6552 references[def.id] = false; 6553 if (def = def.redefined()) references[def.id] = false; 6554 } 6555 }, tw); 6556 if (node.bfinally || (in_try = save_try)) { 6557 walk_body(node.bcatch, tw); 6558 } else { 6559 push(); 6560 walk_body(node.bcatch, tw); 6561 pop(); 6562 } 6563 } 6564 in_try = save_try; 6565 if (node.bfinally) node.bfinally.walk(tw); 6566 return true; 6567 } 6568 if (node instanceof AST_Unary) { 6569 if (!UNARY_POSTFIX[node.operator]) return; 6570 var sym = node.expression; 6571 if (!(sym instanceof AST_SymbolRef)) return; 6572 mark(sym, true); 6573 return true; 6574 } 6575 if (node instanceof AST_VarDef) { 6576 var assigned = node.value; 6577 if (assigned) { 6578 assigned.walk(tw); 6579 } else { 6580 assigned = segment.block instanceof AST_ForEnumeration && segment.block.init === tw.parent(); 6581 } 6582 walk_destructured(AST_SymbolDeclaration, assigned ? function(node) { 6583 if (node instanceof AST_SymbolVar) { 6584 mark(node); 6585 } else { 6586 node.walk(tw); 6587 } 6588 } : function(node) { 6589 if (node instanceof AST_SymbolVar) { 6590 var id = node.definition().id; 6591 var refs = references[id]; 6592 if (refs) { 6593 refs.push(node); 6594 } else if (!(id in references)) { 6595 declarations.add(id, node); 6596 } 6597 } else { 6598 node.walk(tw); 6599 } 6600 }, node.name); 6601 return true; 6602 } 6603 if (node instanceof AST_While) { 6604 push(); 6605 segment.block = node; 6606 segment.loop = true; 6607 descend(); 6608 pop(); 6609 return true; 6610 } 6611 6612 function mark_expression(exp) { 6613 if (!compressor.option("ie")) return; 6614 var sym = root_expr(exp); 6615 if (sym instanceof AST_SymbolRef) sym.walk(tw); 6616 } 6617 6618 function walk_cond(condition, consequent, alternative) { 6619 var save = segment; 6620 var segments = [ save, save ]; 6621 if (condition instanceof AST_Binary) switch (condition.operator) { 6622 case "&&": 6623 segments[0] = walk_cond(condition.left, condition.right)[0]; 6624 break; 6625 case "||": 6626 case "??": 6627 segments[1] = walk_cond(condition.left, null, condition.right)[1]; 6628 break; 6629 default: 6630 condition.walk(tw); 6631 break; 6632 } else if (condition instanceof AST_Conditional) { 6633 walk_cond(condition.condition, condition.consequent, condition.alternative); 6634 } else { 6635 condition.walk(tw); 6636 } 6637 segment = segments[0]; 6638 if (consequent) { 6639 push(); 6640 consequent.walk(tw); 6641 } 6642 segments[0] = segment; 6643 segment = segments[1]; 6644 if (alternative) { 6645 push(); 6646 alternative.walk(tw); 6647 } 6648 segments[1] = segment; 6649 segment = save; 6650 return segments; 6651 } 6652 }); 6653 tw.directives = Object.create(compressor.directives); 6654 self.walk(tw); 6655 var changed = false; 6656 var merged = Object.create(null); 6657 while (first.length && last.length) { 6658 var tail = last.shift(); 6659 if (!tail) continue; 6660 var def = tail.definition; 6661 var tail_refs = references[def.id]; 6662 if (!tail_refs) continue; 6663 tail_refs = { end: tail_refs.end }; 6664 while (def.id in merged) def = merged[def.id]; 6665 tail_refs.start = references[def.id].start; 6666 var skipped = []; 6667 do { 6668 var head = first.shift(); 6669 if (tail.index > head.index) continue; 6670 var prev_def = head.definition; 6671 if (!(prev_def.id in prev)) continue; 6672 var head_refs = references[prev_def.id]; 6673 if (!head_refs) continue; 6674 if (head_refs.start.block !== tail_refs.start.block 6675 || !mergeable(head_refs, tail_refs) 6676 || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs) 6677 || compressor.option("webkit") && is_funarg(def) !== is_funarg(prev_def) 6678 || prev_def.const_redefs 6679 || !all(head_refs.scopes, function(scope) { 6680 return scope.find_variable(def.name) === def; 6681 })) { 6682 skipped.push(head); 6683 continue; 6684 } 6685 head_refs.forEach(function(sym) { 6686 sym.thedef = def; 6687 sym.name = def.name; 6688 if (sym instanceof AST_SymbolRef) { 6689 def.references.push(sym); 6690 prev_def.replaced++; 6691 } else { 6692 def.orig.push(sym); 6693 prev_def.eliminated++; 6694 } 6695 }); 6696 if (!prev_def.fixed) def.fixed = false; 6697 merged[prev_def.id] = def; 6698 changed = true; 6699 break; 6700 } while (first.length); 6701 if (skipped.length) first = skipped.concat(first); 6702 } 6703 return changed; 6704 6705 function push() { 6706 segment = Object.create(segment); 6707 } 6708 6709 function pop() { 6710 segment = Object.getPrototypeOf(segment); 6711 } 6712 6713 function walk_destructured(symbol_type, mark, lhs) { 6714 var marker = new TreeWalker(function(node) { 6715 if (node instanceof AST_Destructured) return; 6716 if (node instanceof AST_DefaultValue) { 6717 push(); 6718 node.value.walk(tw); 6719 pop(); 6720 node.name.walk(marker); 6721 } else if (node instanceof AST_DestructuredKeyVal) { 6722 if (!(node.key instanceof AST_Node)) { 6723 node.value.walk(marker); 6724 } else if (node.value instanceof AST_PropAccess) { 6725 push(); 6726 segment.block = node; 6727 node.key.walk(tw); 6728 node.value.walk(marker); 6729 pop(); 6730 } else { 6731 node.key.walk(tw); 6732 node.value.walk(marker); 6733 } 6734 } else if (node instanceof symbol_type) { 6735 mark(node); 6736 } else { 6737 node.walk(tw); 6738 } 6739 return true; 6740 }); 6741 lhs.walk(marker); 6742 } 6743 6744 function mark(sym, read) { 6745 var def = sym.definition(), ldef; 6746 if (read && !all(in_arg, function(fn) { 6747 ldef = fn.variables.get(sym.name); 6748 if (!ldef) return true; 6749 if (!is_funarg(ldef)) return true; 6750 return ldef !== def 6751 && !def.undeclared 6752 && fn.parent_scope.find_variable(sym.name) !== def; 6753 })) return references[def.id] = references[ldef.id] = false; 6754 var seg = segment; 6755 if (in_try) { 6756 push(); 6757 seg = segment; 6758 pop(); 6759 } 6760 if (def.id in references) { 6761 var refs = references[def.id]; 6762 if (!refs) return; 6763 if (refs.start.block !== seg.block) return references[def.id] = false; 6764 push_ref(sym); 6765 refs.end = seg; 6766 if (def.id in prev) { 6767 last[prev[def.id]] = null; 6768 } else if (!read) { 6769 return; 6770 } 6771 } else if ((ldef = self.variables.get(def.name)) !== def) { 6772 if (ldef && root === seg) references[ldef.id] = false; 6773 return references[def.id] = false; 6774 } else if (compressor.exposed(def) || NO_MERGE[sym.name]) { 6775 return references[def.id] = false; 6776 } else { 6777 var refs = declarations.get(def.id) || []; 6778 refs.scopes = []; 6779 push_ref(sym); 6780 references[def.id] = refs; 6781 if (!read) { 6782 refs.start = seg; 6783 return first.push({ 6784 index: index++, 6785 definition: def, 6786 }); 6787 } 6788 if (seg.block !== self) return references[def.id] = false; 6789 refs.start = root; 6790 } 6791 prev[def.id] = last.length; 6792 last.push({ 6793 index: index++, 6794 definition: def, 6795 }); 6796 6797 function push_ref(sym) { 6798 refs.push(sym); 6799 push_uniq(refs.scopes, sym.scope); 6800 var scope = find_scope(tw); 6801 if (scope !== sym.scope) push_uniq(refs.scopes, scope); 6802 } 6803 } 6804 6805 function insert(target) { 6806 var stack = []; 6807 while (true) { 6808 if (HOP(segment, "block")) { 6809 var block = segment.block; 6810 if (block instanceof AST_LabeledStatement) block = block.body; 6811 if (block === target) break; 6812 } 6813 stack.push(segment); 6814 pop(); 6815 } 6816 segment.inserted = segment.block; 6817 push(); 6818 while (stack.length) { 6819 var seg = stack.pop(); 6820 push(); 6821 if (HOP(seg, "block")) segment.block = seg.block; 6822 if (HOP(seg, "loop")) segment.loop = seg.loop; 6823 } 6824 } 6825 6826 function must_visit(base, segment) { 6827 return base === segment || base.isPrototypeOf(segment); 6828 } 6829 6830 function mergeable(head, tail) { 6831 return must_visit(head.start, head.end) || must_visit(head.start, tail.start); 6832 } 6833 }); 6834 6835 function fill_holes(orig, elements) { 6836 for (var i = elements.length; --i >= 0;) { 6837 if (!elements[i]) elements[i] = make_node(AST_Hole, orig); 6838 } 6839 } 6840 6841 function to_class_expr(defcl, drop_name) { 6842 var cl = make_node(AST_ClassExpression, defcl); 6843 if (cl.name) cl.name = drop_name ? null : make_node(AST_SymbolClass, cl.name); 6844 return cl; 6845 } 6846 6847 function to_func_expr(defun, drop_name) { 6848 var ctor; 6849 switch (defun.CTOR) { 6850 case AST_AsyncDefun: 6851 ctor = AST_AsyncFunction; 6852 break; 6853 case AST_AsyncGeneratorDefun: 6854 ctor = AST_AsyncGeneratorFunction; 6855 break; 6856 case AST_Defun: 6857 ctor = AST_Function; 6858 break; 6859 case AST_GeneratorDefun: 6860 ctor = AST_GeneratorFunction; 6861 break; 6862 } 6863 var fn = make_node(ctor, defun); 6864 fn.name = drop_name ? null : make_node(AST_SymbolLambda, defun.name); 6865 return fn; 6866 } 6867 6868 AST_Scope.DEFMETHOD("drop_unused", function(compressor) { 6869 if (!compressor.option("unused")) return; 6870 var self = this; 6871 var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs; 6872 var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars; 6873 var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) { 6874 var sym, nested = false; 6875 if (node instanceof AST_Assign) { 6876 if (node.write_only || node.operator == "=") sym = extract_reference(node.left, props); 6877 } else if (node instanceof AST_Unary) { 6878 if (node.write_only) sym = extract_reference(node.expression, props); 6879 } 6880 if (!(sym instanceof AST_SymbolRef)) return; 6881 var def = sym.definition(); 6882 if (export_defaults[def.id]) return; 6883 if (compressor.exposed(def)) return; 6884 if (!can_drop_symbol(sym, compressor, nested)) return; 6885 return sym; 6886 6887 function extract_reference(node, props) { 6888 if (node instanceof AST_PropAccess) { 6889 var expr = node.expression; 6890 if (!expr.may_throw_on_access(compressor, true)) { 6891 nested = true; 6892 if (props && node instanceof AST_Sub) props.unshift(node.property); 6893 return extract_reference(expr, props); 6894 } 6895 } else if (node instanceof AST_Assign && node.operator == "=") { 6896 node.write_only = "p"; 6897 var ref = extract_reference(node.right); 6898 if (!props) return ref; 6899 props.assign = node; 6900 return ref instanceof AST_SymbolRef ? ref : node.left; 6901 } 6902 return node; 6903 } 6904 }; 6905 var assign_in_use = Object.create(null); 6906 var export_defaults = Object.create(null); 6907 var find_variable = function(name) { 6908 find_variable = compose(self, 0, noop); 6909 return find_variable(name); 6910 6911 function compose(child, level, find) { 6912 var parent = compressor.parent(level); 6913 if (!parent) return find; 6914 var in_arg = parent instanceof AST_Lambda && member(child, parent.argnames); 6915 return compose(parent, level + 1, in_arg ? function(name) { 6916 var def = find(name); 6917 if (def) return def; 6918 def = parent.variables.get(name); 6919 if (def) { 6920 var sym = def.orig[0]; 6921 if (sym instanceof AST_SymbolFunarg || sym instanceof AST_SymbolLambda) return def; 6922 } 6923 } : parent.variables ? function(name) { 6924 return find(name) || parent.variables.get(name); 6925 } : find); 6926 } 6927 }; 6928 var for_ins = Object.create(null); 6929 var in_use = []; 6930 var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use 6931 var lambda_ids = Object.create(null); 6932 var value_read = Object.create(null); 6933 var value_modified = Object.create(null); 6934 var var_defs = Object.create(null); 6935 if (self instanceof AST_Toplevel && compressor.top_retain) { 6936 self.variables.each(function(def) { 6937 if (compressor.top_retain(def) && !(def.id in in_use_ids)) { 6938 AST_Node.info("Retaining variable {name}", def); 6939 in_use_ids[def.id] = true; 6940 in_use.push(def); 6941 } 6942 }); 6943 } 6944 var assignments = new Dictionary(); 6945 var initializations = new Dictionary(); 6946 // pass 1: find out which symbols are directly used in 6947 // this scope (not in nested scopes). 6948 var scope = this; 6949 var tw = new TreeWalker(function(node, descend) { 6950 if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) { 6951 node.each_argname(function(argname) { 6952 var def = argname.definition(); 6953 if (!(def.id in in_use_ids)) { 6954 in_use_ids[def.id] = true; 6955 in_use.push(def); 6956 } 6957 }); 6958 } 6959 if (node === self) return; 6960 if (scope === self) { 6961 if (node instanceof AST_DefClass) { 6962 var def = node.name.definition(); 6963 var drop = drop_funcs && !def.exported; 6964 if (!drop && !(def.id in in_use_ids)) { 6965 in_use_ids[def.id] = true; 6966 in_use.push(def); 6967 } 6968 var used = tw.parent() instanceof AST_ExportDefault; 6969 if (used) { 6970 export_defaults[def.id] = true; 6971 } else if (drop && !(def.id in lambda_ids)) { 6972 lambda_ids[def.id] = 1; 6973 } 6974 if (node.extends) node.extends.walk(tw); 6975 var values = []; 6976 node.properties.forEach(function(prop) { 6977 if (prop.key instanceof AST_Node) prop.key.walk(tw); 6978 var value = prop.value; 6979 if (!value) return; 6980 if (is_static_field_or_init(prop)) { 6981 if (!used && value.contains_this()) used = true; 6982 walk_class_prop(value); 6983 } else { 6984 values.push(value); 6985 } 6986 }); 6987 values.forEach(drop && used ? walk_class_prop : function(value) { 6988 initializations.add(def.id, value); 6989 }); 6990 return true; 6991 } 6992 if (node instanceof AST_LambdaDefinition) { 6993 var def = node.name.definition(); 6994 var drop = drop_funcs && !def.exported; 6995 if (!drop && !(def.id in in_use_ids)) { 6996 in_use_ids[def.id] = true; 6997 in_use.push(def); 6998 } 6999 initializations.add(def.id, node); 7000 if (tw.parent() instanceof AST_ExportDefault) { 7001 export_defaults[def.id] = true; 7002 return scan_ref_scoped(node, descend, true); 7003 } 7004 if (drop && !(def.id in lambda_ids)) lambda_ids[def.id] = 1; 7005 return true; 7006 } 7007 if (node instanceof AST_Definitions) { 7008 node.definitions.forEach(function(defn) { 7009 var value = defn.value; 7010 var side_effects = value 7011 && (defn.name instanceof AST_Destructured || value.has_side_effects(compressor)); 7012 var shared = side_effects && value.tail_node().operator == "="; 7013 defn.name.mark_symbol(function(name) { 7014 if (!(name instanceof AST_SymbolDeclaration)) return; 7015 var def = name.definition(); 7016 var_defs[def.id] = (var_defs[def.id] || 0) + 1; 7017 if (node instanceof AST_Var && def.orig[0] instanceof AST_SymbolCatch) { 7018 var redef = def.redefined(); 7019 if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1; 7020 } 7021 if (!(def.id in in_use_ids) && (!drop_vars || def.exported 7022 || (node instanceof AST_Const ? def.redefined() : def.const_redefs) 7023 || !(node instanceof AST_Var || is_safe_lexical(def)))) { 7024 in_use_ids[def.id] = true; 7025 in_use.push(def); 7026 } 7027 if (value) { 7028 if (!side_effects) { 7029 initializations.add(def.id, value); 7030 } else if (shared) { 7031 verify_safe_usage(def, name, value_modified[def.id]); 7032 } 7033 assignments.add(def.id, defn); 7034 } 7035 unmark_lambda(def); 7036 return true; 7037 }, tw); 7038 if (side_effects) value.walk(tw); 7039 }); 7040 return true; 7041 } 7042 if (node instanceof AST_SymbolFunarg) { 7043 var def = node.definition(); 7044 var_defs[def.id] = (var_defs[def.id] || 0) + 1; 7045 assignments.add(def.id, node); 7046 return true; 7047 } 7048 if (node instanceof AST_SymbolImport) { 7049 var def = node.definition(); 7050 if (!(def.id in in_use_ids) && (!drop_vars || !is_safe_lexical(def))) { 7051 in_use_ids[def.id] = true; 7052 in_use.push(def); 7053 } 7054 return true; 7055 } 7056 } 7057 return scan_ref_scoped(node, descend, true); 7058 7059 function walk_class_prop(value) { 7060 var save_scope = scope; 7061 scope = node; 7062 value.walk(tw); 7063 scope = save_scope; 7064 } 7065 }); 7066 tw.directives = Object.create(compressor.directives); 7067 self.walk(tw); 7068 var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie") ? function(def) { 7069 return !compressor.exposed(def) && def.references.length == def.replaced; 7070 } : function(def) { 7071 if (!(def.id in in_use_ids)) return true; 7072 if (def.orig.length - def.eliminated < 2) return false; 7073 // function argument will always overshadow its name 7074 if (def.orig[1] instanceof AST_SymbolFunarg) return true; 7075 // retain if referenced within destructured object of argument 7076 return all(def.references, function(ref) { 7077 return !ref.in_arg; 7078 }); 7079 }; 7080 if (compressor.option("ie")) initializations.each(function(init, id) { 7081 if (id in in_use_ids) return; 7082 init.forEach(function(init) { 7083 init.walk(new TreeWalker(function(node) { 7084 if (node instanceof AST_Function && node.name && !drop_fn_name(node.name.definition())) { 7085 node.walk(tw); 7086 return true; 7087 } 7088 if (node instanceof AST_Scope) return true; 7089 })); 7090 }); 7091 }); 7092 // pass 2: for every used symbol we need to walk its 7093 // initialization code to figure out if it uses other 7094 // symbols (that may not be in_use). 7095 tw = new TreeWalker(scan_ref_scoped); 7096 for (var i = 0; i < in_use.length; i++) { 7097 var init = initializations.get(in_use[i].id); 7098 if (init) init.forEach(function(init) { 7099 init.walk(tw); 7100 }); 7101 } 7102 Object.keys(assign_in_use).forEach(function(id) { 7103 var assigns = assign_in_use[id]; 7104 if (!assigns) { 7105 delete assign_in_use[id]; 7106 return; 7107 } 7108 assigns = assigns.reduce(function(in_use, assigns) { 7109 assigns.forEach(function(assign) { 7110 push_uniq(in_use, assign); 7111 }); 7112 return in_use; 7113 }, []); 7114 var in_use = (assignments.get(id) || []).filter(function(node) { 7115 return find_if(node instanceof AST_Unary ? function(assign) { 7116 return assign === node; 7117 } : function(assign) { 7118 if (assign === node) return true; 7119 if (assign instanceof AST_Unary) return false; 7120 return get_rvalue(assign) === get_rvalue(node); 7121 }, assigns); 7122 }); 7123 if (assigns.length == in_use.length) { 7124 assign_in_use[id] = in_use; 7125 } else { 7126 delete assign_in_use[id]; 7127 } 7128 }); 7129 // pass 3: we should drop declarations not in_use 7130 var calls_to_drop_args = []; 7131 var fns_with_marked_args = []; 7132 var trimmer = new TreeTransformer(function(node) { 7133 if (node instanceof AST_DefaultValue) return trim_default(trimmer, node); 7134 if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer); 7135 if (node instanceof AST_DestructuredArray) { 7136 var trim = !node.rest; 7137 for (var i = node.elements.length; --i >= 0;) { 7138 var element = node.elements[i].transform(trimmer); 7139 if (element) { 7140 node.elements[i] = element; 7141 trim = false; 7142 } else if (trim) { 7143 node.elements.pop(); 7144 } else { 7145 node.elements[i] = make_node(AST_Hole, node.elements[i]); 7146 } 7147 } 7148 return node; 7149 } 7150 if (node instanceof AST_DestructuredObject) { 7151 var properties = []; 7152 node.properties.forEach(function(prop) { 7153 var retain = false; 7154 if (prop.key instanceof AST_Node) { 7155 prop.key = prop.key.transform(tt); 7156 retain = prop.key.has_side_effects(compressor); 7157 } 7158 if ((retain || node.rest) && is_decl(prop.value)) { 7159 prop.value = prop.value.transform(tt); 7160 properties.push(prop); 7161 } else { 7162 var value = prop.value.transform(trimmer); 7163 if (!value && node.rest) { 7164 if (prop.value instanceof AST_DestructuredArray) { 7165 value = make_node(AST_DestructuredArray, prop.value, { elements: [] }); 7166 } else { 7167 value = make_node(AST_DestructuredObject, prop.value, { properties: [] }); 7168 } 7169 } 7170 if (value) { 7171 prop.value = value; 7172 properties.push(prop); 7173 } 7174 } 7175 }); 7176 node.properties = properties; 7177 return node; 7178 } 7179 if (node instanceof AST_SymbolDeclaration) return trim_decl(node); 7180 }); 7181 var tt = new TreeTransformer(function(node, descend, in_list) { 7182 var parent = tt.parent(); 7183 if (drop_vars) { 7184 var props = [], sym = assign_as_unused(node, props); 7185 if (sym) { 7186 var value; 7187 if (can_drop_lhs(sym, node)) { 7188 if (node instanceof AST_Assign) { 7189 value = get_rhs(node); 7190 if (node.write_only === true) value = value.drop_side_effect_free(compressor); 7191 } 7192 if (!value) value = make_node(AST_Number, node, { value: 0 }); 7193 } 7194 if (value) { 7195 if (props.assign) { 7196 var assign = props.assign.drop_side_effect_free(compressor); 7197 if (assign) { 7198 assign.write_only = true; 7199 props.unshift(assign); 7200 } 7201 } 7202 if (!(parent instanceof AST_Sequence) 7203 || parent.tail_node() === node 7204 || value.has_side_effects(compressor)) { 7205 props.push(value); 7206 } 7207 switch (props.length) { 7208 case 0: 7209 return List.skip; 7210 case 1: 7211 return maintain_this_binding(parent, node, props[0].transform(tt)); 7212 default: 7213 return make_sequence(node, props.map(function(prop) { 7214 return prop.transform(tt); 7215 })); 7216 } 7217 } 7218 } else if (node instanceof AST_UnaryPostfix 7219 && node.expression instanceof AST_SymbolRef 7220 && indexOf_assign(node.expression.definition(), node) < 0) { 7221 return make_node(AST_UnaryPrefix, node, { 7222 operator: "+", 7223 expression: node.expression, 7224 }); 7225 } 7226 } 7227 if (node instanceof AST_Binary && node.operator == "instanceof") { 7228 var sym = node.right; 7229 if (!(sym instanceof AST_SymbolRef)) return; 7230 if (sym.definition().id in in_use_ids) return; 7231 var lhs = node.left.drop_side_effect_free(compressor); 7232 var value = make_node(AST_False, node).optimize(compressor); 7233 return lhs ? make_sequence(node, [ lhs, value ]) : value; 7234 } 7235 if (node instanceof AST_Call) { 7236 calls_to_drop_args.push(node); 7237 node.args = node.args.map(function(arg) { 7238 return arg.transform(tt); 7239 }); 7240 node.expression = node.expression.transform(tt); 7241 return node; 7242 } 7243 if (scope !== self) return; 7244 if (drop_funcs && node !== self && node instanceof AST_DefClass) { 7245 var def = node.name.definition(); 7246 if (!(def.id in in_use_ids)) { 7247 log(node.name, "Dropping unused class {name}"); 7248 def.eliminated++; 7249 descend(node, tt); 7250 var trimmed = to_class_expr(node, true); 7251 if (parent instanceof AST_ExportDefault) return trimmed; 7252 trimmed = trimmed.drop_side_effect_free(compressor, true); 7253 if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed }); 7254 return in_list ? List.skip : make_node(AST_EmptyStatement, node); 7255 } 7256 } 7257 if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) { 7258 node.name = null; 7259 } 7260 if (node instanceof AST_Lambda) { 7261 if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) { 7262 var def = node.name.definition(); 7263 if (!(def.id in in_use_ids)) { 7264 log(node.name, "Dropping unused function {name}"); 7265 def.eliminated++; 7266 if (parent instanceof AST_ExportDefault) { 7267 descend_scope(); 7268 return to_func_expr(node, true); 7269 } 7270 return in_list ? List.skip : make_node(AST_EmptyStatement, node); 7271 } 7272 } 7273 descend_scope(); 7274 if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) { 7275 node.name = null; 7276 } 7277 if (!(node instanceof AST_Accessor)) { 7278 var args, spread, trim = compressor.drop_fargs(node, parent); 7279 if (trim && parent instanceof AST_Call && parent.expression === node) { 7280 args = parent.args; 7281 for (spread = 0; spread < args.length; spread++) { 7282 if (args[spread] instanceof AST_Spread) break; 7283 } 7284 } 7285 var argnames = node.argnames; 7286 var rest = node.rest; 7287 var after = false, before = false; 7288 if (rest) { 7289 before = true; 7290 if (!args || spread < argnames.length || rest instanceof AST_SymbolFunarg) { 7291 rest = rest.transform(trimmer); 7292 } else { 7293 var trimmed = trim_destructured(rest, make_node(AST_Array, parent, { 7294 elements: args.slice(argnames.length), 7295 }), trim_decl, !node.uses_arguments, rest); 7296 rest = trimmed.name; 7297 args.length = argnames.length; 7298 if (trimmed.value.elements.length) [].push.apply(args, trimmed.value.elements); 7299 } 7300 if (rest instanceof AST_Destructured && !rest.rest) { 7301 if (rest instanceof AST_DestructuredArray) { 7302 if (rest.elements.length == 0) rest = null; 7303 } else if (rest.properties.length == 0) { 7304 rest = null; 7305 } 7306 } 7307 node.rest = rest; 7308 if (rest) { 7309 trim = false; 7310 after = true; 7311 } 7312 } 7313 var default_length = trim ? -1 : node.length(); 7314 var trim_value = args && !node.uses_arguments && parent !== compressor.parent(); 7315 for (var i = argnames.length; --i >= 0;) { 7316 var sym = argnames[i]; 7317 if (sym instanceof AST_SymbolFunarg) { 7318 var def = sym.definition(); 7319 if (def.id in in_use_ids) { 7320 trim = false; 7321 if (indexOf_assign(def, sym) < 0) sym.unused = null; 7322 } else if (trim) { 7323 log(sym, "Dropping unused function argument {name}"); 7324 argnames.pop(); 7325 def.eliminated++; 7326 sym.unused = true; 7327 } else { 7328 sym.unused = true; 7329 } 7330 } else { 7331 before = true; 7332 var funarg; 7333 if (!args || spread < i) { 7334 funarg = sym.transform(trimmer); 7335 } else { 7336 var trimmed = trim_destructured(sym, args[i], trim_decl, trim_value, sym); 7337 funarg = trimmed.name; 7338 if (trimmed.value) args[i] = trimmed.value; 7339 } 7340 if (funarg) { 7341 trim = false; 7342 argnames[i] = funarg; 7343 if (!after) after = !(funarg instanceof AST_SymbolFunarg); 7344 } else if (trim) { 7345 log_default(sym, "Dropping unused default argument {name}"); 7346 argnames.pop(); 7347 } else if (i > default_length) { 7348 log_default(sym, "Dropping unused default argument assignment {name}"); 7349 if (sym.name instanceof AST_SymbolFunarg) { 7350 sym.name.unused = true; 7351 } else { 7352 after = true; 7353 } 7354 argnames[i] = sym.name; 7355 } else { 7356 log_default(sym, "Dropping unused default argument value {name}"); 7357 argnames[i] = sym = sym.clone(); 7358 sym.value = make_node(AST_Number, sym, { value: 0 }); 7359 after = true; 7360 } 7361 } 7362 } 7363 if (before && !after && node.uses_arguments && !tt.has_directive("use strict")) { 7364 node.rest = make_node(AST_DestructuredArray, node, { elements: [] }); 7365 } 7366 fns_with_marked_args.push(node); 7367 } 7368 return node; 7369 } 7370 if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) { 7371 node.argname.transform(trimmer); 7372 } 7373 if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) { 7374 // place uninitialized names at the start 7375 var body = [], head = [], tail = []; 7376 // for unused names whose initialization has 7377 // side effects, we can cascade the init. code 7378 // into the next one, or next statement. 7379 var side_effects = []; 7380 var duplicated = 0; 7381 var is_var = node instanceof AST_Var; 7382 node.definitions.forEach(function(def) { 7383 if (def.value) def.value = def.value.transform(tt); 7384 var value = def.value; 7385 if (def.name instanceof AST_Destructured) { 7386 var trimmed = trim_destructured(def.name, value, function(node) { 7387 if (!drop_vars) return node; 7388 if (node.definition().id in in_use_ids) return node; 7389 if (is_catch(node)) return node; 7390 if (is_var && !can_drop_symbol(node)) return node; 7391 return null; 7392 }, true); 7393 if (trimmed.name) { 7394 def = make_node(AST_VarDef, def, { 7395 name: trimmed.name, 7396 value: value = trimmed.value, 7397 }); 7398 flush(); 7399 } else if (trimmed.value) { 7400 side_effects.push(trimmed.value); 7401 } 7402 return; 7403 } 7404 var sym = def.name.definition(); 7405 var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym); 7406 if (!drop_sym || !drop_vars || sym.id in in_use_ids) { 7407 var index; 7408 if (value && ((index = indexOf_assign(sym, def)) < 0 || self_assign(value.tail_node()))) { 7409 def = def.clone(); 7410 value = value.drop_side_effect_free(compressor); 7411 if (value) AST_Node.warn("Side effects in definition of variable {name} [{start}]", def.name); 7412 if (node instanceof AST_Const) { 7413 def.value = value || make_node(AST_Number, def, { value: 0 }); 7414 } else { 7415 def.value = null; 7416 if (value) side_effects.push(value); 7417 } 7418 value = null; 7419 if (index >= 0) assign_in_use[sym.id][index] = def; 7420 } 7421 var old_def, fn; 7422 if (!value && !(node instanceof AST_Let)) { 7423 if (parent instanceof AST_ExportDeclaration) { 7424 flush(); 7425 } else if (drop_sym && var_defs[sym.id] > 1) { 7426 AST_Node.info("Dropping declaration of variable {name} [{start}]", def.name); 7427 var_defs[sym.id]--; 7428 sym.eliminated++; 7429 } else { 7430 head.push(def); 7431 } 7432 } else if (compressor.option("functions") 7433 && !compressor.option("ie") 7434 && drop_sym 7435 && value 7436 && var_defs[sym.id] == 1 7437 && sym.assignments == 0 7438 && (fn = value.tail_node()) instanceof AST_LambdaExpression 7439 && !is_arguments(sym) 7440 && !is_arrow(fn) 7441 && assigned_once(fn, sym.references) 7442 && can_declare_defun(fn) 7443 && (old_def = rename_def(fn, def.name.name)) !== false) { 7444 AST_Node.warn("Declaring {name} as function [{start}]", def.name); 7445 var ctor; 7446 switch (fn.CTOR) { 7447 case AST_AsyncFunction: 7448 ctor = AST_AsyncDefun; 7449 break; 7450 case AST_AsyncGeneratorFunction: 7451 ctor = AST_AsyncGeneratorDefun; 7452 break; 7453 case AST_Function: 7454 ctor = AST_Defun; 7455 break; 7456 case AST_GeneratorFunction: 7457 ctor = AST_GeneratorDefun; 7458 break; 7459 } 7460 var defun = make_node(ctor, fn); 7461 defun.name = make_node(AST_SymbolDefun, def.name); 7462 var name_def = def.name.scope.resolve().def_function(defun.name); 7463 if (old_def) old_def.forEach(function(node) { 7464 node.name = name_def.name; 7465 node.thedef = name_def; 7466 node.reference(); 7467 }); 7468 body.push(defun); 7469 if (value !== fn) [].push.apply(side_effects, value.expressions.slice(0, -1)); 7470 } else { 7471 if (drop_sym 7472 && var_defs[sym.id] > 1 7473 && !(parent instanceof AST_ExportDeclaration) 7474 && sym.orig.indexOf(def.name) > sym.eliminated) { 7475 var_defs[sym.id]--; 7476 duplicated++; 7477 } 7478 flush(); 7479 } 7480 } else if (is_catch(def.name)) { 7481 value = value && value.drop_side_effect_free(compressor); 7482 if (value) side_effects.push(value); 7483 if (var_defs[sym.id] > 1) { 7484 AST_Node.warn("Dropping duplicated declaration of variable {name} [{start}]", def.name); 7485 var_defs[sym.id]--; 7486 sym.eliminated++; 7487 } else { 7488 def.value = null; 7489 head.push(def); 7490 } 7491 } else { 7492 value = value && value.drop_side_effect_free(compressor); 7493 if (value) { 7494 AST_Node.warn("Side effects in initialization of unused variable {name} [{start}]", def.name); 7495 side_effects.push(value); 7496 } else { 7497 log(def.name, "Dropping unused variable {name}"); 7498 } 7499 sym.eliminated++; 7500 } 7501 7502 function self_assign(ref) { 7503 return ref instanceof AST_SymbolRef && ref.definition() === sym; 7504 } 7505 7506 function assigned_once(fn, refs) { 7507 if (refs.length == 0) return fn === def.name.fixed_value(); 7508 return all(refs, function(ref) { 7509 return fn === ref.fixed_value(); 7510 }); 7511 } 7512 7513 function can_declare_defun(fn) { 7514 if (!is_var || compressor.has_directive("use strict") || !(fn instanceof AST_Function)) { 7515 return parent instanceof AST_Scope; 7516 } 7517 return parent instanceof AST_Block 7518 || parent instanceof AST_For && parent.init === node 7519 || parent instanceof AST_If; 7520 } 7521 7522 function rename_def(fn, name) { 7523 if (!fn.name) return null; 7524 var def = fn.name.definition(); 7525 if (def.orig.length > 1) return null; 7526 if (def.assignments > 0) return false; 7527 if (def.name == name) return def; 7528 if (compressor.option("keep_fnames")) return false; 7529 var forbidden; 7530 switch (name) { 7531 case "await": 7532 forbidden = is_async; 7533 break; 7534 case "yield": 7535 forbidden = is_generator; 7536 break; 7537 } 7538 return all(def.references, function(ref) { 7539 var scope = ref.scope; 7540 if (scope.find_variable(name) !== sym) return false; 7541 if (forbidden) do { 7542 scope = scope.resolve(); 7543 if (forbidden(scope)) return false; 7544 } while (scope !== fn && (scope = scope.parent_scope)); 7545 return true; 7546 }) && def; 7547 } 7548 7549 function is_catch(node) { 7550 var sym = node.definition(); 7551 return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve(); 7552 } 7553 7554 function flush() { 7555 if (side_effects.length > 0) { 7556 if (tail.length == 0) { 7557 body.push(make_node(AST_SimpleStatement, node, { 7558 body: make_sequence(node, side_effects), 7559 })); 7560 } else if (value) { 7561 side_effects.push(value); 7562 def.value = make_sequence(value, side_effects); 7563 } else { 7564 def.value = make_node(AST_UnaryPrefix, def, { 7565 operator: "void", 7566 expression: make_sequence(def, side_effects), 7567 }); 7568 } 7569 side_effects = []; 7570 } 7571 tail.push(def); 7572 } 7573 }); 7574 switch (head.length) { 7575 case 0: 7576 if (tail.length == 0) break; 7577 if (tail.length == duplicated) { 7578 [].unshift.apply(side_effects, tail.map(function(def) { 7579 AST_Node.info("Dropping duplicated definition of variable {name} [{start}]", def.name); 7580 var sym = def.name.definition(); 7581 var ref = make_node(AST_SymbolRef, def.name); 7582 sym.references.push(ref); 7583 var assign = make_node(AST_Assign, def, { 7584 operator: "=", 7585 left: ref, 7586 right: def.value, 7587 }); 7588 var index = indexOf_assign(sym, def); 7589 if (index >= 0) assign_in_use[sym.id][index] = assign; 7590 sym.assignments++; 7591 sym.eliminated++; 7592 return assign; 7593 })); 7594 break; 7595 } 7596 case 1: 7597 if (tail.length == 0) { 7598 var id = head[0].name.definition().id; 7599 if (id in for_ins) { 7600 node.definitions = head; 7601 for_ins[id].init = node; 7602 break; 7603 } 7604 } 7605 default: 7606 var seq; 7607 if (tail.length > 0 && (seq = tail[0].value) instanceof AST_Sequence) { 7608 tail[0].value = seq.tail_node(); 7609 body.push(make_node(AST_SimpleStatement, node, { 7610 body: make_sequence(seq, seq.expressions.slice(0, -1)), 7611 })); 7612 } 7613 node.definitions = head.concat(tail); 7614 body.push(node); 7615 } 7616 if (side_effects.length > 0) { 7617 body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) })); 7618 } 7619 return insert_statements(body, node, in_list); 7620 } 7621 if (node instanceof AST_Assign) { 7622 descend(node, tt); 7623 if (!(node.left instanceof AST_Destructured)) return node; 7624 var trimmed = trim_destructured(node.left, node.right, function(node) { 7625 return node; 7626 }, node.write_only === true); 7627 if (trimmed.name) return make_node(AST_Assign, node, { 7628 operator: node.operator, 7629 left: trimmed.name, 7630 right: trimmed.value, 7631 }); 7632 if (trimmed.value) return trimmed.value; 7633 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return List.skip; 7634 return make_node(AST_Number, node, { value: 0 }); 7635 } 7636 if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) { 7637 // Certain combination of unused name + side effect leads to invalid AST: 7638 // https://github.com/mishoo/UglifyJS/issues/1830 7639 // We fix it at this stage by moving the label inwards, back to the `for`. 7640 descend(node, tt); 7641 if (node.body instanceof AST_BlockStatement) { 7642 var block = node.body; 7643 node.body = block.body.pop(); 7644 block.body.push(node); 7645 return in_list ? List.splice(block.body) : block; 7646 } 7647 return node; 7648 } 7649 if (node instanceof AST_Scope) { 7650 descend_scope(); 7651 return node; 7652 } 7653 if (node instanceof AST_SymbolImport) { 7654 if (!compressor.option("imports") || node.definition().id in in_use_ids) return node; 7655 return in_list ? List.skip : null; 7656 } 7657 7658 function descend_scope() { 7659 var save_scope = scope; 7660 scope = node; 7661 descend(node, tt); 7662 scope = save_scope; 7663 } 7664 }, function(node, in_list) { 7665 if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list); 7666 if (node instanceof AST_ExportDeclaration) { 7667 var block = node.body; 7668 if (!(block instanceof AST_BlockStatement)) return; 7669 node.body = block.body.pop(); 7670 block.body.push(node); 7671 return in_list ? List.splice(block.body) : block; 7672 } 7673 if (node instanceof AST_For) return patch_for_init(node, in_list); 7674 if (node instanceof AST_ForIn) { 7675 if (!drop_vars || !compressor.option("loops")) return; 7676 if (!is_empty(node.body)) return; 7677 var sym = get_init_symbol(node); 7678 if (!sym) return; 7679 var def = sym.definition(); 7680 if (def.id in in_use_ids) return; 7681 log(sym, "Dropping unused loop variable {name}"); 7682 if (for_ins[def.id] === node) delete for_ins[def.id]; 7683 var body = []; 7684 var value = node.object.drop_side_effect_free(compressor); 7685 if (value) { 7686 AST_Node.warn("Side effects in object of for-in loop [{start}]", value); 7687 body.push(make_node(AST_SimpleStatement, node, { body: value })); 7688 } 7689 if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) { 7690 body.push(node.init); 7691 } 7692 return insert_statements(body, node, in_list); 7693 } 7694 if (node instanceof AST_Import) { 7695 if (node.properties && node.properties.length == 0) node.properties = null; 7696 return node; 7697 } 7698 if (node instanceof AST_Sequence) { 7699 if (node.expressions.length > 1) return; 7700 return maintain_this_binding(tt.parent(), node, node.expressions[0]); 7701 } 7702 }); 7703 tt.push(compressor.parent()); 7704 tt.directives = Object.create(compressor.directives); 7705 self.transform(tt); 7706 if (self instanceof AST_Lambda 7707 && self.body.length == 1 7708 && self.body[0] instanceof AST_Directive 7709 && self.body[0].value == "use strict") { 7710 self.body.length = 0; 7711 } 7712 calls_to_drop_args.forEach(function(call) { 7713 drop_unused_call_args(call, compressor, fns_with_marked_args); 7714 }); 7715 7716 function log(sym, text) { 7717 AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{start}]", sym); 7718 } 7719 7720 function log_default(node, text) { 7721 if (node.name instanceof AST_SymbolFunarg) { 7722 log(node.name, text); 7723 } else { 7724 AST_Node.info(text + " [{start}]", node); 7725 } 7726 } 7727 7728 function get_rvalue(expr) { 7729 return expr[expr instanceof AST_Assign ? "right" : "value"]; 7730 } 7731 7732 function insert_statements(body, orig, in_list) { 7733 switch (body.length) { 7734 case 0: 7735 return in_list ? List.skip : make_node(AST_EmptyStatement, orig); 7736 case 1: 7737 return body[0]; 7738 default: 7739 return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, { body: body }); 7740 } 7741 } 7742 7743 function track_assigns(def, node) { 7744 if (def.scope.resolve() !== self) return false; 7745 if (!def.fixed || !node.fixed) assign_in_use[def.id] = false; 7746 return assign_in_use[def.id] !== false; 7747 } 7748 7749 function add_assigns(def, node) { 7750 if (!assign_in_use[def.id]) assign_in_use[def.id] = []; 7751 if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns); 7752 } 7753 7754 function indexOf_assign(def, node) { 7755 var nodes = assign_in_use[def.id]; 7756 return nodes && nodes.indexOf(node); 7757 } 7758 7759 function unmark_lambda(def) { 7760 if (lambda_ids[def.id] > 1 && !(def.id in in_use_ids)) { 7761 in_use_ids[def.id] = true; 7762 in_use.push(def); 7763 } 7764 lambda_ids[def.id] = 0; 7765 } 7766 7767 function verify_safe_usage(def, read, modified) { 7768 if (def.id in in_use_ids) return; 7769 if (read && modified) { 7770 in_use_ids[def.id] = read; 7771 in_use.push(def); 7772 } else { 7773 value_read[def.id] = read; 7774 value_modified[def.id] = modified; 7775 } 7776 } 7777 7778 function can_drop_lhs(sym, node) { 7779 var def = sym.definition(); 7780 var in_use = in_use_ids[def.id]; 7781 if (!in_use) return true; 7782 if (node[node instanceof AST_Assign ? "left" : "expression"] !== sym) return false; 7783 return in_use === sym && def.references.length - def.replaced == 1 || indexOf_assign(def, node) < 0; 7784 } 7785 7786 function get_rhs(assign) { 7787 var rhs = assign.right; 7788 if (!assign.write_only) return rhs; 7789 if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs; 7790 if (!(rhs.left instanceof AST_SymbolRef)) return rhs; 7791 if (!(assign.left instanceof AST_SymbolRef)) return rhs; 7792 var def = assign.left.definition(); 7793 if (rhs.left.definition() !== def) return rhs; 7794 if (rhs.right.has_side_effects(compressor)) return rhs; 7795 if (track_assigns(def, rhs.left)) add_assigns(def, rhs.left); 7796 return rhs.right; 7797 } 7798 7799 function get_init_symbol(for_in) { 7800 var init = for_in.init; 7801 if (init instanceof AST_Definitions) { 7802 init = init.definitions[0].name; 7803 return init instanceof AST_SymbolDeclaration && init; 7804 } 7805 while (init instanceof AST_PropAccess) init = init.expression.tail_node(); 7806 if (init instanceof AST_SymbolRef) return init; 7807 } 7808 7809 function scan_ref_scoped(node, descend, init) { 7810 if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) { 7811 var def = node.left.definition(); 7812 if (def.scope.resolve() === self) assignments.add(def.id, node); 7813 } 7814 if (node instanceof AST_SymbolRef && node.in_arg) var_defs[node.definition().id] = 0; 7815 if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) { 7816 var def = node.expression.definition(); 7817 if (def.scope.resolve() === self) assignments.add(def.id, node); 7818 } 7819 var props = [], sym = assign_as_unused(node, props); 7820 if (sym) { 7821 var node_def = sym.definition(); 7822 if (node_def.scope.resolve() !== self && self.variables.get(sym.name) !== node_def) return; 7823 if (is_arguments(node_def) && !all(self.argnames, function(argname) { 7824 return !argname.match_symbol(function(node) { 7825 if (node instanceof AST_SymbolFunarg) { 7826 var def = node.definition(); 7827 return def.references.length > def.replaced; 7828 } 7829 }, true); 7830 })) return; 7831 if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return; 7832 var assign = props.assign; 7833 if (assign) { 7834 assign.write_only = true; 7835 assign.walk(tw); 7836 } 7837 props.forEach(function(prop) { 7838 prop.walk(tw); 7839 }); 7840 if (node instanceof AST_Assign) { 7841 var right = get_rhs(node), shared = false; 7842 if (init && node.write_only === true && !right.has_side_effects(compressor)) { 7843 initializations.add(node_def.id, right); 7844 } else { 7845 right.walk(tw); 7846 shared = right.tail_node().operator == "="; 7847 } 7848 if (node.left === sym) { 7849 if (!node.write_only || shared) { 7850 verify_safe_usage(node_def, sym, value_modified[node_def.id]); 7851 } 7852 } else { 7853 var fixed = sym.fixed_value(); 7854 if (!fixed || !fixed.is_constant()) { 7855 verify_safe_usage(node_def, value_read[node_def.id], true); 7856 } 7857 } 7858 } 7859 if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym); 7860 unmark_lambda(node_def); 7861 return true; 7862 } 7863 if (node instanceof AST_Binary) { 7864 if (node.operator != "instanceof") return; 7865 var sym = node.right; 7866 if (!(sym instanceof AST_SymbolRef)) return; 7867 var id = sym.definition().id; 7868 if (!lambda_ids[id]) return; 7869 node.left.walk(tw); 7870 lambda_ids[id]++; 7871 return true; 7872 } 7873 if (node instanceof AST_ForIn) { 7874 if (node.init instanceof AST_SymbolRef && scope === self) { 7875 var id = node.init.definition().id; 7876 if (!(id in for_ins)) for_ins[id] = node; 7877 } 7878 if (!drop_vars || !compressor.option("loops")) return; 7879 if (!is_empty(node.body)) return; 7880 if (node.init.has_side_effects(compressor)) return; 7881 var sym = get_init_symbol(node); 7882 if (!sym) return; 7883 var def = sym.definition(); 7884 if (def.scope.resolve() !== self) { 7885 var d = find_variable(sym.name); 7886 if (d === def || d && d.redefined() === def) return; 7887 } 7888 node.object.walk(tw); 7889 return true; 7890 } 7891 if (node instanceof AST_SymbolRef) { 7892 var node_def = node.definition(); 7893 if (!(node_def.id in in_use_ids)) { 7894 in_use_ids[node_def.id] = true; 7895 in_use.push(node_def); 7896 } 7897 if (cross_scope(node_def.scope, node.scope)) { 7898 var redef = node_def.redefined(); 7899 if (redef && !(redef.id in in_use_ids)) { 7900 in_use_ids[redef.id] = true; 7901 in_use.push(redef); 7902 } 7903 } 7904 if (track_assigns(node_def, node)) add_assigns(node_def, node); 7905 return true; 7906 } 7907 if (node instanceof AST_Scope) { 7908 var save_scope = scope; 7909 scope = node; 7910 descend(); 7911 scope = save_scope; 7912 return true; 7913 } 7914 } 7915 7916 function is_decl(node) { 7917 return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration; 7918 } 7919 7920 function trim_decl(node) { 7921 if (node.definition().id in in_use_ids) return node; 7922 if (node instanceof AST_SymbolFunarg) node.unused = true; 7923 return null; 7924 } 7925 7926 function trim_default(trimmer, node) { 7927 node.value = node.value.transform(tt); 7928 var name = node.name.transform(trimmer); 7929 if (!name) { 7930 if (node.name instanceof AST_Destructured) return null; 7931 var value = node.value.drop_side_effect_free(compressor); 7932 if (!value) return null; 7933 log(node.name, "Side effects in default value of unused variable {name}"); 7934 node = node.clone(); 7935 node.name.unused = null; 7936 node.value = value; 7937 } 7938 return node; 7939 } 7940 7941 function trim_destructured(node, value, process, drop, root) { 7942 var trimmer = new TreeTransformer(function(node) { 7943 if (node instanceof AST_DefaultValue) { 7944 if (!(compressor.option("default_values") && value && value.is_defined(compressor))) { 7945 var save_drop = drop; 7946 drop = false; 7947 var trimmed = trim_default(trimmer, node); 7948 drop = save_drop; 7949 if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor); 7950 return trimmed; 7951 } else if (node === root) { 7952 root = node = node.name; 7953 } else { 7954 node = node.name; 7955 } 7956 } 7957 if (node instanceof AST_DestructuredArray) { 7958 var save_drop = drop; 7959 var save_value = value; 7960 if (value instanceof AST_SymbolRef) { 7961 drop = false; 7962 value = value.fixed_value(); 7963 } 7964 var native, values; 7965 if (value instanceof AST_Array) { 7966 native = true; 7967 values = value.elements; 7968 } else { 7969 native = value && value.is_string(compressor); 7970 values = false; 7971 } 7972 var elements = [], newValues = drop && [], pos = 0; 7973 node.elements.forEach(function(element, index) { 7974 value = values && values[index]; 7975 if (value instanceof AST_Hole) { 7976 value = null; 7977 } else if (value instanceof AST_Spread) { 7978 if (drop) { 7979 newValues.length = pos; 7980 fill_holes(save_value, newValues); 7981 [].push.apply(newValues, values.slice(index)); 7982 save_value.elements = newValues; 7983 } 7984 value = values = false; 7985 } 7986 element = element.transform(trimmer); 7987 if (element) elements[pos] = element; 7988 if (drop && value) newValues[pos] = value; 7989 if (element || value || !drop || !values) pos++; 7990 }); 7991 value = values && make_node(AST_Array, save_value, { 7992 elements: values.slice(node.elements.length), 7993 }); 7994 if (node.rest) { 7995 var was_drop = drop; 7996 drop = false; 7997 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt); 7998 drop = was_drop; 7999 if (node.rest) elements.length = pos; 8000 } 8001 if (drop) { 8002 if (value && !node.rest) value = value.drop_side_effect_free(compressor); 8003 if (value instanceof AST_Array) { 8004 value = value.elements; 8005 } else if (value instanceof AST_Sequence) { 8006 value = value.expressions; 8007 } else if (value) { 8008 value = [ value ]; 8009 } 8010 if (value && value.length) { 8011 newValues.length = pos; 8012 [].push.apply(newValues, value); 8013 } 8014 } 8015 value = save_value; 8016 drop = save_drop; 8017 if (values && newValues) { 8018 fill_holes(value, newValues); 8019 value = value.clone(); 8020 value.elements = newValues; 8021 } 8022 if (!native) { 8023 elements.length = node.elements.length; 8024 } else if (!node.rest) switch (elements.length) { 8025 case 0: 8026 if (node === root) break; 8027 if (drop) value = value.drop_side_effect_free(compressor); 8028 return null; 8029 case 1: 8030 if (!drop) break; 8031 if (node === root) break; 8032 var sym = elements[0]; 8033 if (sym.has_side_effects(compressor)) break; 8034 if (value.has_side_effects(compressor) && sym.match_symbol(function(node) { 8035 return node instanceof AST_PropAccess; 8036 })) break; 8037 value = make_node(AST_Sub, node, { 8038 expression: value, 8039 property: make_node(AST_Number, node, { value: 0 }), 8040 }); 8041 return sym; 8042 } 8043 fill_holes(node, elements); 8044 node.elements = elements; 8045 return node; 8046 } 8047 if (node instanceof AST_DestructuredObject) { 8048 var save_drop = drop; 8049 var save_value = value; 8050 if (value instanceof AST_SymbolRef) { 8051 drop = false; 8052 value = value.fixed_value(); 8053 } 8054 var prop_keys, prop_map, values; 8055 if (value instanceof AST_Object) { 8056 prop_keys = []; 8057 prop_map = new Dictionary(); 8058 values = value.properties.map(function(prop, index) { 8059 prop = prop.clone(); 8060 if (prop instanceof AST_Spread) { 8061 prop_map = false; 8062 } else { 8063 var key = prop.key; 8064 if (key instanceof AST_Node) key = key.evaluate(compressor, true); 8065 if (key instanceof AST_Node) { 8066 prop_map = false; 8067 } else if (prop_map && !(prop instanceof AST_ObjectSetter)) { 8068 prop_map.set(key, prop); 8069 } 8070 prop_keys[index] = key; 8071 } 8072 return prop; 8073 }); 8074 } 8075 if (node.rest) { 8076 value = false; 8077 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt); 8078 } 8079 var can_drop = new Dictionary(); 8080 var drop_keys = drop && new Dictionary(); 8081 var properties = []; 8082 node.properties.map(function(prop) { 8083 var key = prop.key; 8084 if (key instanceof AST_Node) { 8085 prop.key = key = key.transform(tt); 8086 key = key.evaluate(compressor, true); 8087 } 8088 if (key instanceof AST_Node) { 8089 drop_keys = false; 8090 } else { 8091 can_drop.set(key, !can_drop.has(key)); 8092 } 8093 return key; 8094 }).forEach(function(key, index) { 8095 var prop = node.properties[index], trimmed; 8096 if (key instanceof AST_Node) { 8097 drop = false; 8098 value = false; 8099 trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value); 8100 } else { 8101 drop = drop_keys && can_drop.get(key); 8102 var mapped = prop_map && prop_map.get(key); 8103 if (mapped) { 8104 value = mapped.value; 8105 if (value instanceof AST_Accessor) value = false; 8106 } else { 8107 value = false; 8108 } 8109 trimmed = prop.value.transform(trimmer); 8110 if (!trimmed) { 8111 if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value); 8112 if (drop_keys && !drop_keys.has(key)) { 8113 if (mapped) { 8114 drop_keys.set(key, mapped); 8115 if (value === null) { 8116 prop_map.set(key, retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, { 8117 key: mapped.key, 8118 value: make_node(AST_Number, mapped, { value: 0 }), 8119 })); 8120 } 8121 } else { 8122 drop_keys.set(key, true); 8123 } 8124 } 8125 } else if (drop_keys) { 8126 drop_keys.set(key, false); 8127 } 8128 if (value) mapped.value = value; 8129 } 8130 if (trimmed) { 8131 prop.value = trimmed; 8132 properties.push(prop); 8133 } 8134 }); 8135 value = save_value; 8136 drop = save_drop; 8137 if (drop_keys && prop_keys) { 8138 value = value.clone(); 8139 value.properties = List(values, function(prop, index) { 8140 if (prop instanceof AST_Spread) return prop; 8141 var key = prop_keys[index]; 8142 if (key instanceof AST_Node) return prop; 8143 if (drop_keys.has(key)) { 8144 var mapped = drop_keys.get(key); 8145 if (!mapped) return prop; 8146 if (mapped === prop) return prop_map.get(key) || List.skip; 8147 } else if (node.rest) { 8148 return prop; 8149 } 8150 var trimmed = prop.value.drop_side_effect_free(compressor); 8151 if (trimmed) { 8152 prop.value = trimmed; 8153 return prop; 8154 } 8155 return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, { 8156 key: prop.key, 8157 value: make_node(AST_Number, prop, { value: 0 }), 8158 }) : List.skip; 8159 }); 8160 } 8161 if (value && !node.rest) switch (properties.length) { 8162 case 0: 8163 if (node === root) break; 8164 if (value.may_throw_on_access(compressor, true)) break; 8165 if (drop) value = value.drop_side_effect_free(compressor); 8166 return null; 8167 case 1: 8168 if (!drop) break; 8169 if (node === root) break; 8170 var prop = properties[0]; 8171 if (prop.key instanceof AST_Node) break; 8172 if (prop.value.has_side_effects(compressor)) break; 8173 if (value.has_side_effects(compressor) && prop.value.match_symbol(function(node) { 8174 return node instanceof AST_PropAccess; 8175 })) break; 8176 value = make_node(AST_Sub, node, { 8177 expression: value, 8178 property: make_node_from_constant(prop.key, prop), 8179 }); 8180 return prop.value; 8181 } 8182 node.properties = properties; 8183 return node; 8184 } 8185 if (node instanceof AST_Hole) { 8186 node = null; 8187 } else { 8188 node = process(node); 8189 } 8190 if (!node && drop && value) value = value.drop_side_effect_free(compressor); 8191 return node; 8192 }); 8193 return { 8194 name: node.transform(trimmer), 8195 value: value, 8196 }; 8197 8198 function retain_key(prop) { 8199 return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor); 8200 } 8201 8202 function clear_write_only(node) { 8203 if (node instanceof AST_Assign) { 8204 node.write_only = false; 8205 clear_write_only(node.right); 8206 } else if (node instanceof AST_Binary) { 8207 if (!lazy_op[node.operator]) return; 8208 clear_write_only(node.left); 8209 clear_write_only(node.right); 8210 } else if (node instanceof AST_Conditional) { 8211 clear_write_only(node.consequent); 8212 clear_write_only(node.alternative); 8213 } else if (node instanceof AST_Sequence) { 8214 clear_write_only(node.tail_node()); 8215 } else if (node instanceof AST_Unary) { 8216 node.write_only = false; 8217 } 8218 } 8219 8220 function retain_lhs(node) { 8221 if (node instanceof AST_DefaultValue) return retain_lhs(node.name); 8222 if (node instanceof AST_Destructured) { 8223 if (value === null) { 8224 value = make_node(AST_Number, node, { value: 0 }); 8225 } else if (value) { 8226 if (value.may_throw_on_access(compressor, true)) { 8227 value = make_node(AST_Array, node, { 8228 elements: value instanceof AST_Sequence ? value.expressions : [ value ], 8229 }); 8230 } else { 8231 clear_write_only(value); 8232 } 8233 } 8234 return make_node(AST_DestructuredObject, node, { properties: [] }); 8235 } 8236 node.unused = null; 8237 return node; 8238 } 8239 } 8240 }); 8241 8242 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) { 8243 if (compressor.has_directive("use asm")) return; 8244 var hoist_funs = compressor.option("hoist_funs"); 8245 var hoist_vars = compressor.option("hoist_vars"); 8246 var self = this; 8247 if (hoist_vars) { 8248 // let's count var_decl first, we seem to waste a lot of 8249 // space if we hoist `var` when there's only one. 8250 var var_decl = 0; 8251 self.walk(new TreeWalker(function(node) { 8252 if (var_decl > 1) return true; 8253 if (node instanceof AST_ExportDeclaration) return true; 8254 if (node instanceof AST_Scope && node !== self) return true; 8255 if (node instanceof AST_Var) { 8256 var_decl++; 8257 return true; 8258 } 8259 })); 8260 if (var_decl <= 1) hoist_vars = false; 8261 } 8262 if (!hoist_funs && !hoist_vars) return; 8263 var consts = new Dictionary(); 8264 var dirs = []; 8265 var hoisted = []; 8266 var vars = new Dictionary(); 8267 var tt = new TreeTransformer(function(node, descend, in_list) { 8268 if (node === self) return; 8269 if (node instanceof AST_Directive) { 8270 dirs.push(node); 8271 return in_list ? List.skip : make_node(AST_EmptyStatement, node); 8272 } 8273 if (node instanceof AST_LambdaDefinition) { 8274 if (!hoist_funs) return node; 8275 var p = tt.parent(); 8276 if (p instanceof AST_ExportDeclaration) return node; 8277 if (p instanceof AST_ExportDefault) return node; 8278 if (p !== self && compressor.has_directive("use strict")) return node; 8279 hoisted.push(node); 8280 return in_list ? List.skip : make_node(AST_EmptyStatement, node); 8281 } 8282 if (node instanceof AST_Var) { 8283 if (!hoist_vars) return node; 8284 var p = tt.parent(); 8285 if (p instanceof AST_ExportDeclaration) return node; 8286 if (!all(node.definitions, function(defn) { 8287 var sym = defn.name; 8288 return sym instanceof AST_SymbolVar 8289 && !consts.has(sym.name) 8290 && self.find_variable(sym.name) === sym.definition(); 8291 })) return node; 8292 node.definitions.forEach(function(defn) { 8293 vars.set(defn.name.name, defn); 8294 }); 8295 var seq = node.to_assignments(); 8296 if (p instanceof AST_ForEnumeration && p.init === node) { 8297 if (seq) return seq; 8298 var sym = node.definitions[0].name; 8299 return make_node(AST_SymbolRef, sym); 8300 } 8301 if (p instanceof AST_For && p.init === node) return seq; 8302 if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node); 8303 return make_node(AST_SimpleStatement, node, { body: seq }); 8304 } 8305 if (node instanceof AST_Scope) return node; 8306 if (node instanceof AST_SymbolConst) { 8307 consts.set(node.name, true); 8308 return node; 8309 } 8310 }); 8311 self.transform(tt); 8312 if (vars.size() > 0) { 8313 // collect only vars which don't show up in self's arguments list 8314 var defns = []; 8315 if (self instanceof AST_Lambda) self.each_argname(function(argname) { 8316 if (all(argname.definition().references, function(ref) { 8317 return !ref.in_arg; 8318 })) vars.del(argname.name); 8319 }); 8320 vars.each(function(defn, name) { 8321 defn = defn.clone(); 8322 defn.name = defn.name.clone(); 8323 defn.value = null; 8324 defns.push(defn); 8325 vars.set(name, defn); 8326 defn.name.definition().orig.unshift(defn.name); 8327 }); 8328 if (defns.length > 0) hoisted.push(make_node(AST_Var, self, { definitions: defns })); 8329 } 8330 self.body = dirs.concat(hoisted, self.body); 8331 }); 8332 8333 function scan_local_returns(fn, transform) { 8334 fn.walk(new TreeWalker(function(node) { 8335 if (node instanceof AST_Return) { 8336 transform(node); 8337 return true; 8338 } 8339 if (node instanceof AST_Scope && node !== fn) return true; 8340 })); 8341 } 8342 8343 function map_self_returns(fn) { 8344 var map = Object.create(null); 8345 scan_local_returns(fn, function(node) { 8346 var value = node.value; 8347 if (value) value = value.tail_node(); 8348 if (value instanceof AST_SymbolRef) { 8349 var id = value.definition().id; 8350 map[id] = (map[id] || 0) + 1; 8351 } 8352 }); 8353 return map; 8354 } 8355 8356 function can_trim_returns(def, self_returns, compressor) { 8357 if (compressor.exposed(def)) return false; 8358 switch (def.references.length - def.replaced - (self_returns[def.id] || 0)) { 8359 case def.drop_return: 8360 return "d"; 8361 case def.bool_return: 8362 return true; 8363 } 8364 } 8365 8366 function process_boolean_returns(fn, compressor) { 8367 scan_local_returns(fn, function(node) { 8368 node.in_bool = true; 8369 var value = node.value; 8370 if (value) { 8371 var ev = fuzzy_eval(compressor, value); 8372 if (!ev) { 8373 value = value.drop_side_effect_free(compressor); 8374 node.value = value ? make_sequence(node.value, [ 8375 value, 8376 make_node(AST_Number, node.value, { value: 0 }), 8377 ]) : null; 8378 } else if (!(ev instanceof AST_Node)) { 8379 value = value.drop_side_effect_free(compressor); 8380 node.value = value ? make_sequence(node.value, [ 8381 value, 8382 make_node(AST_Number, node.value, { value: 1 }), 8383 ]) : make_node(AST_Number, node.value, { value: 1 }); 8384 } 8385 } 8386 }); 8387 } 8388 8389 AST_Scope.DEFMETHOD("process_returns", noop); 8390 AST_Defun.DEFMETHOD("process_returns", function(compressor) { 8391 if (!compressor.option("booleans")) return; 8392 if (compressor.parent() instanceof AST_ExportDefault) return; 8393 switch (can_trim_returns(this.name.definition(), map_self_returns(this), compressor)) { 8394 case "d": 8395 drop_returns(compressor, this, true); 8396 break; 8397 case true: 8398 process_boolean_returns(this, compressor); 8399 break; 8400 } 8401 }); 8402 AST_Function.DEFMETHOD("process_returns", function(compressor) { 8403 if (!compressor.option("booleans")) return; 8404 var drop = true; 8405 var self_returns = map_self_returns(this); 8406 if (this.name && !can_trim(this.name.definition())) return; 8407 var parent = compressor.parent(); 8408 if (parent instanceof AST_Assign) { 8409 if (parent.operator != "=") return; 8410 var sym = parent.left; 8411 if (!(sym instanceof AST_SymbolRef)) return; 8412 if (!can_trim(sym.definition())) return; 8413 } else if (parent instanceof AST_Call && parent.expression !== this) { 8414 var exp = parent.expression; 8415 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value(); 8416 if (!(exp instanceof AST_Lambda)) return; 8417 if (exp.uses_arguments || exp.pinned()) return; 8418 var args = parent.args, sym; 8419 for (var i = 0; i < args.length; i++) { 8420 var arg = args[i]; 8421 if (arg === this) { 8422 sym = exp.argnames[i]; 8423 if (!sym && exp.rest) return; 8424 break; 8425 } 8426 if (arg instanceof AST_Spread) return; 8427 } 8428 if (sym instanceof AST_DefaultValue) sym = sym.name; 8429 if (sym instanceof AST_SymbolFunarg && !can_trim(sym.definition())) return; 8430 } else if (parent.TYPE == "Call") { 8431 compressor.pop(); 8432 var in_bool = compressor.in_boolean_context(); 8433 compressor.push(this); 8434 switch (in_bool) { 8435 case true: 8436 drop = false; 8437 case "d": 8438 break; 8439 default: 8440 return; 8441 } 8442 } else return; 8443 if (drop) { 8444 drop_returns(compressor, this, true); 8445 } else { 8446 process_boolean_returns(this, compressor); 8447 } 8448 8449 function can_trim(def) { 8450 switch (can_trim_returns(def, self_returns, compressor)) { 8451 case true: 8452 drop = false; 8453 case "d": 8454 return true; 8455 } 8456 } 8457 }); 8458 8459 AST_BlockScope.DEFMETHOD("var_names", function() { 8460 var var_names = this._var_names; 8461 if (!var_names) { 8462 this._var_names = var_names = new Dictionary(); 8463 this.enclosed.forEach(function(def) { 8464 var_names.set(def.name, true); 8465 }); 8466 this.variables.each(function(def, name) { 8467 var_names.set(name, true); 8468 }); 8469 } 8470 return var_names; 8471 }); 8472 8473 AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) { 8474 var scopes = [ this ]; 8475 if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) { 8476 var s = ref.scope; 8477 do { 8478 if (!push_uniq(scopes, s)) return; 8479 s = s.parent_scope; 8480 } while (s && s !== this); 8481 }); 8482 prefix = prefix.replace(/^[^a-z_$]|[^a-z0-9_$]/gi, "_"); 8483 var name = prefix; 8484 for (var i = 0; !all(scopes, function(scope) { 8485 return !scope.var_names().has(name); 8486 }); i++) name = prefix + "$" + i; 8487 var sym = make_node(type, orig, { 8488 name: name, 8489 scope: this, 8490 }); 8491 var def = this.def_variable(sym); 8492 scopes.forEach(function(scope) { 8493 scope.enclosed.push(def); 8494 scope.var_names().set(name, true); 8495 }); 8496 return sym; 8497 }); 8498 8499 AST_Scope.DEFMETHOD("hoist_properties", function(compressor) { 8500 if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return; 8501 var self = this; 8502 if (is_arrow(self) && self.value) return; 8503 var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false; 8504 var defs_by_id = Object.create(null); 8505 var tt = new TreeTransformer(function(node, descend) { 8506 if (node instanceof AST_Assign) { 8507 if (node.operator != "=") return; 8508 if (!node.write_only) return; 8509 if (!can_hoist(node.left, node.right, 1)) return; 8510 descend(node, tt); 8511 var defs = new Dictionary(); 8512 var assignments = []; 8513 var decls = []; 8514 node.right.properties.forEach(function(prop) { 8515 var decl = make_sym(AST_SymbolVar, node.left, prop.key); 8516 decls.push(make_node(AST_VarDef, node, { 8517 name: decl, 8518 value: null, 8519 })); 8520 var sym = make_node(AST_SymbolRef, node, { 8521 name: decl.name, 8522 scope: self, 8523 thedef: decl.definition(), 8524 }); 8525 sym.reference(); 8526 assignments.push(make_node(AST_Assign, node, { 8527 operator: "=", 8528 left: sym, 8529 right: prop.value, 8530 })); 8531 }); 8532 defs.value = node.right; 8533 defs_by_id[node.left.definition().id] = defs; 8534 self.body.splice(self.body.indexOf(tt.stack[1]) + 1, 0, make_node(AST_Var, node, { 8535 definitions: decls, 8536 })); 8537 return make_sequence(node, assignments); 8538 } 8539 if (node instanceof AST_Scope) { 8540 if (node === self) return; 8541 var parent = tt.parent(); 8542 if (parent.TYPE == "Call" && parent.expression === node) return; 8543 return node; 8544 } 8545 if (node instanceof AST_VarDef) { 8546 if (!can_hoist(node.name, node.value, 0)) return; 8547 descend(node, tt); 8548 var defs = new Dictionary(); 8549 var var_defs = []; 8550 var decl = node.clone(); 8551 decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null; 8552 var_defs.push(decl); 8553 node.value.properties.forEach(function(prop) { 8554 var_defs.push(make_node(AST_VarDef, node, { 8555 name: make_sym(node.name.CTOR, node.name, prop.key), 8556 value: prop.value, 8557 })); 8558 }); 8559 defs.value = node.value; 8560 defs_by_id[node.name.definition().id] = defs; 8561 return List.splice(var_defs); 8562 } 8563 8564 function make_sym(type, sym, key) { 8565 var new_var = self.make_var(type, sym, sym.name + "_" + key); 8566 defs.set(key, new_var.definition()); 8567 return new_var; 8568 } 8569 }); 8570 self.transform(tt); 8571 self.transform(new TreeTransformer(function(node, descend) { 8572 if (node instanceof AST_PropAccess) { 8573 if (!(node.expression instanceof AST_SymbolRef)) return; 8574 var defs = defs_by_id[node.expression.definition().id]; 8575 if (!defs) return; 8576 if (node.expression.fixed_value() !== defs.value) return; 8577 var def = defs.get(node.get_property()); 8578 var sym = make_node(AST_SymbolRef, node, { 8579 name: def.name, 8580 scope: node.expression.scope, 8581 thedef: def, 8582 }); 8583 sym.reference(); 8584 return sym; 8585 } 8586 if (node instanceof AST_SymbolRef) { 8587 var defs = defs_by_id[node.definition().id]; 8588 if (!defs) return; 8589 if (node.fixed_value() !== defs.value) return; 8590 return make_node(AST_Object, node, { properties: [] }); 8591 } 8592 })); 8593 8594 function can_hoist(sym, right, count) { 8595 if (!(sym instanceof AST_Symbol)) return; 8596 var def = sym.definition(); 8597 if (def.assignments != count) return; 8598 if (def.references.length - def.replaced == count) return; 8599 if (def.single_use) return; 8600 if (self.find_variable(sym.name) !== def) return; 8601 if (top_retain(def)) return; 8602 if (sym.fixed_value() !== right) return; 8603 var fixed = sym.fixed || def.fixed; 8604 if (fixed.direct_access) return; 8605 if (fixed.escaped && fixed.escaped.depth == 1) return; 8606 return right instanceof AST_Object 8607 && right.properties.length > 0 8608 && can_drop_symbol(sym, compressor) 8609 && all(right.properties, function(prop) { 8610 return can_hoist_property(prop) && prop.key !== "__proto__"; 8611 }); 8612 } 8613 }); 8614 8615 function fn_name_unused(fn, compressor) { 8616 if (!fn.name || !compressor.option("ie")) return true; 8617 var def = fn.name.definition(); 8618 if (compressor.exposed(def)) return false; 8619 return all(def.references, function(sym) { 8620 return !(sym instanceof AST_SymbolRef); 8621 }); 8622 } 8623 8624 function drop_returns(compressor, exp, ignore_name) { 8625 if (!(exp instanceof AST_Lambda)) return; 8626 var arrow = is_arrow(exp); 8627 var async = is_async(exp); 8628 var changed = false; 8629 var drop_body = false; 8630 if (arrow && compressor.option("arrows")) { 8631 if (!exp.value) { 8632 drop_body = true; 8633 } else if (!async || needs_enqueuing(compressor, exp.value)) { 8634 var dropped = exp.value.drop_side_effect_free(compressor); 8635 if (dropped !== exp.value) { 8636 changed = true; 8637 exp.value = dropped; 8638 } 8639 } 8640 } else if (!is_generator(exp)) { 8641 if (!ignore_name && exp.name) { 8642 var def = exp.name.definition(); 8643 drop_body = def.references.length == def.replaced; 8644 } else { 8645 drop_body = true; 8646 } 8647 } 8648 if (drop_body) { 8649 exp.process_expression(false, function(node) { 8650 var value = node.value; 8651 if (value) { 8652 if (async && !needs_enqueuing(compressor, value)) return node; 8653 value = value.drop_side_effect_free(compressor, true); 8654 } 8655 changed = true; 8656 if (!value) return make_node(AST_EmptyStatement, node); 8657 return make_node(AST_SimpleStatement, node, { body: value }); 8658 }); 8659 scan_local_returns(exp, function(node) { 8660 var value = node.value; 8661 if (value) { 8662 if (async && !needs_enqueuing(compressor, value)) return; 8663 var dropped = value.drop_side_effect_free(compressor); 8664 if (dropped !== value) { 8665 changed = true; 8666 if (dropped && async && !needs_enqueuing(compressor, dropped)) { 8667 dropped = dropped.negate(compressor); 8668 } 8669 node.value = dropped; 8670 } 8671 } 8672 }); 8673 } 8674 if (async && compressor.option("awaits")) { 8675 if (drop_body) exp.process_expression("awaits", function(node) { 8676 var body = node.body; 8677 if (body instanceof AST_Await) { 8678 if (needs_enqueuing(compressor, body.expression)) { 8679 changed = true; 8680 body = body.expression.drop_side_effect_free(compressor, true); 8681 if (!body) return make_node(AST_EmptyStatement, node); 8682 node.body = body; 8683 } 8684 } else if (body instanceof AST_Sequence) { 8685 var exprs = body.expressions; 8686 for (var i = exprs.length; --i >= 0;) { 8687 var tail = exprs[i]; 8688 if (!(tail instanceof AST_Await)) break; 8689 var value = tail.expression; 8690 if (!needs_enqueuing(compressor, value)) break; 8691 changed = true; 8692 if (exprs[i] = value.drop_side_effect_free(compressor)) break; 8693 } 8694 switch (i) { 8695 case -1: 8696 return make_node(AST_EmptyStatement, node); 8697 case 0: 8698 node.body = exprs[0]; 8699 break; 8700 default: 8701 exprs.length = i + 1; 8702 break; 8703 } 8704 } 8705 return node; 8706 }); 8707 var abort = !drop_body && exp.name || arrow && exp.value && !needs_enqueuing(compressor, exp.value); 8708 var tw = new TreeWalker(function(node) { 8709 if (abort) return true; 8710 if (tw.parent() === exp && node.may_throw(compressor)) return abort = true; 8711 if (node instanceof AST_Await) return abort = true; 8712 if (node instanceof AST_ForAwaitOf) return abort = true; 8713 if (node instanceof AST_Return) { 8714 if (node.value && !needs_enqueuing(compressor, node.value)) return abort = true; 8715 return; 8716 } 8717 if (node instanceof AST_Scope && node !== exp) return true; 8718 }); 8719 exp.walk(tw); 8720 if (!abort) { 8721 var ctor; 8722 switch (exp.CTOR) { 8723 case AST_AsyncArrow: 8724 ctor = AST_Arrow; 8725 break; 8726 case AST_AsyncFunction: 8727 ctor = AST_Function; 8728 break; 8729 case AST_AsyncGeneratorFunction: 8730 ctor = AST_GeneratorFunction; 8731 break; 8732 } 8733 return make_node(ctor, exp); 8734 } 8735 } 8736 return changed && exp.clone(); 8737 } 8738 8739 // drop_side_effect_free() 8740 // remove side-effect-free parts which only affects return value 8741 (function(def) { 8742 // Drop side-effect-free elements from an array of expressions. 8743 // Returns an array of expressions with side-effects or null 8744 // if all elements were dropped. Note: original array may be 8745 // returned if nothing changed. 8746 function trim(nodes, compressor, first_in_statement, spread) { 8747 var len = nodes.length; 8748 var ret = [], changed = false; 8749 for (var i = 0; i < len; i++) { 8750 var node = nodes[i]; 8751 var trimmed; 8752 if (spread && node instanceof AST_Spread) { 8753 trimmed = spread(node, compressor, first_in_statement); 8754 } else { 8755 trimmed = node.drop_side_effect_free(compressor, first_in_statement); 8756 } 8757 if (trimmed !== node) changed = true; 8758 if (trimmed) { 8759 ret.push(trimmed); 8760 first_in_statement = false; 8761 } 8762 } 8763 return ret.length ? changed ? ret : nodes : null; 8764 } 8765 function array_spread(node, compressor, first_in_statement) { 8766 var exp = node.expression; 8767 if (!exp.is_string(compressor)) return node; 8768 return exp.drop_side_effect_free(compressor, first_in_statement); 8769 } 8770 function convert_spread(node) { 8771 return node instanceof AST_Spread ? make_node(AST_Array, node, { elements: [ node ] }) : node; 8772 } 8773 def(AST_Node, return_this); 8774 def(AST_Accessor, return_null); 8775 def(AST_Array, function(compressor, first_in_statement) { 8776 var values = trim(this.elements, compressor, first_in_statement, array_spread); 8777 if (!values) return null; 8778 if (values === this.elements && all(values, function(node) { 8779 return node instanceof AST_Spread; 8780 })) return this; 8781 return make_sequence(this, values.map(convert_spread)); 8782 }); 8783 def(AST_Assign, function(compressor) { 8784 var left = this.left; 8785 if (left instanceof AST_PropAccess) { 8786 var expr = left.expression; 8787 if (expr.may_throw_on_access(compressor, true)) return this; 8788 if (compressor.has_directive("use strict") && expr.is_constant()) return this; 8789 } 8790 if (left.has_side_effects(compressor)) return this; 8791 if (lazy_op[this.operator.slice(0, -1)]) return this; 8792 this.write_only = true; 8793 if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this; 8794 return this.right.drop_side_effect_free(compressor); 8795 }); 8796 def(AST_Await, function(compressor) { 8797 if (!compressor.option("awaits")) return this; 8798 var exp = this.expression; 8799 if (!needs_enqueuing(compressor, exp)) return this; 8800 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") exp = exp.expression; 8801 var dropped = exp.drop_side_effect_free(compressor); 8802 if (dropped === exp) return this; 8803 if (!dropped) { 8804 dropped = make_node(AST_Number, exp, { value: 0 }); 8805 } else if (!needs_enqueuing(compressor, dropped)) { 8806 dropped = dropped.negate(compressor); 8807 } 8808 var node = this.clone(); 8809 node.expression = dropped; 8810 return node; 8811 }); 8812 def(AST_Binary, function(compressor, first_in_statement) { 8813 var left = this.left; 8814 var right = this.right; 8815 var op = this.operator; 8816 if (!can_drop_op(op, right, compressor)) { 8817 var lhs = left.drop_side_effect_free(compressor, first_in_statement); 8818 if (lhs === left) return this; 8819 var node = this.clone(); 8820 node.left = lhs || make_node(AST_Number, left, { value: 0 }); 8821 return node; 8822 } 8823 var rhs = right.drop_side_effect_free(compressor, first_in_statement); 8824 if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement); 8825 if (lazy_op[op] && rhs.has_side_effects(compressor)) { 8826 var node = this; 8827 if (rhs !== right) { 8828 node = node.clone(); 8829 node.right = rhs.drop_side_effect_free(compressor); 8830 } 8831 if (op == "??") return node; 8832 var negated = node.clone(); 8833 negated.operator = op == "&&" ? "||" : "&&"; 8834 negated.left = left.negate(compressor, first_in_statement); 8835 var negated_rhs = negated.right.tail_node(); 8836 if (negated_rhs instanceof AST_Binary && negated.operator == negated_rhs.operator) swap_chain(negated); 8837 var best = first_in_statement ? best_of_statement : best_of_expression; 8838 return op == "&&" ? best(node, negated) : best(negated, node); 8839 } 8840 var lhs = left.drop_side_effect_free(compressor, first_in_statement); 8841 if (!lhs) return rhs; 8842 rhs = rhs.drop_side_effect_free(compressor); 8843 if (!rhs) return lhs; 8844 return make_sequence(this, [ lhs, rhs ]); 8845 }); 8846 function assign_this_only(fn, compressor) { 8847 fn.new = true; 8848 var result = all(fn.body, function(stat) { 8849 return !stat.has_side_effects(compressor); 8850 }) && all(fn.argnames, function(argname) { 8851 return !argname.match_symbol(return_false); 8852 }) && !(fn.rest && fn.rest.match_symbol(return_false)); 8853 fn.new = false; 8854 return result; 8855 } 8856 def(AST_Call, function(compressor, first_in_statement) { 8857 var self = this; 8858 if (self.is_expr_pure(compressor)) { 8859 if (self.pure) AST_Node.warn("Dropping __PURE__ call [{start}]", self); 8860 var args = trim(self.args, compressor, first_in_statement, array_spread); 8861 return args && make_sequence(self, args.map(convert_spread)); 8862 } 8863 var exp = self.expression; 8864 if (self.is_call_pure(compressor)) { 8865 var exprs = self.args.slice(); 8866 exprs.unshift(exp.expression); 8867 exprs = trim(exprs, compressor, first_in_statement, array_spread); 8868 return exprs && make_sequence(self, exprs.map(convert_spread)); 8869 } 8870 if (compressor.option("yields") && is_generator(exp)) { 8871 var call = self.clone(); 8872 call.expression = make_node(AST_Function, exp); 8873 call.expression.body = []; 8874 var opt = call.transform(compressor); 8875 if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement); 8876 } 8877 var dropped = drop_returns(compressor, exp); 8878 if (dropped) { 8879 // always shallow clone to ensure stripping of negated IIFEs 8880 self = self.clone(); 8881 self.expression = dropped; 8882 // avoid extraneous traversal 8883 if (exp._squeezed) self.expression._squeezed = true; 8884 } 8885 if (self instanceof AST_New) { 8886 var fn = exp; 8887 if (fn instanceof AST_SymbolRef) fn = fn.fixed_value(); 8888 if (fn instanceof AST_Lambda) { 8889 if (assign_this_only(fn, compressor)) { 8890 var exprs = self.args.slice(); 8891 exprs.unshift(exp); 8892 exprs = trim(exprs, compressor, first_in_statement, array_spread); 8893 return exprs && make_sequence(self, exprs.map(convert_spread)); 8894 } 8895 if (!fn.contains_this()) { 8896 self = make_node(AST_Call, self); 8897 self.expression = self.expression.clone(); 8898 self.args = self.args.slice(); 8899 } 8900 } 8901 } 8902 self.call_only = true; 8903 return self; 8904 }); 8905 def(AST_ClassExpression, function(compressor, first_in_statement) { 8906 var self = this; 8907 var exprs = [], values = [], init = 0; 8908 var props = self.properties; 8909 for (var i = 0; i < props.length; i++) { 8910 var prop = props[i]; 8911 if (prop.key instanceof AST_Node) exprs.push(prop.key); 8912 if (!is_static_field_or_init(prop)) continue; 8913 var value = prop.value; 8914 if (!value.has_side_effects(compressor)) continue; 8915 if (value.contains_this()) return self; 8916 if (prop instanceof AST_ClassInit) { 8917 init++; 8918 values.push(prop); 8919 } else { 8920 values.push(value); 8921 } 8922 } 8923 var base = self.extends; 8924 if (base) { 8925 if (base instanceof AST_SymbolRef) base = base.fixed_value(); 8926 base = !safe_for_extends(base); 8927 if (!base) exprs.unshift(self.extends); 8928 } 8929 exprs = trim(exprs, compressor, first_in_statement); 8930 if (exprs) first_in_statement = false; 8931 values = trim(values, compressor, first_in_statement); 8932 if (!exprs) { 8933 if (!base && !values && !self.name) return null; 8934 exprs = []; 8935 } 8936 if (base || self.name || !compressor.has_directive("use strict")) { 8937 var node = to_class_expr(self); 8938 if (!base) node.extends = null; 8939 node.properties = []; 8940 if (values) { 8941 if (values.length == init) { 8942 if (exprs.length) values.unshift(make_node(AST_ClassField, self, { 8943 key: make_sequence(self, exprs), 8944 value: null, 8945 })); 8946 node.properties = values; 8947 } else node.properties.push(make_node(AST_ClassField, self, { 8948 static: true, 8949 key: exprs.length ? make_sequence(self, exprs) : "c", 8950 value: make_value(), 8951 })); 8952 } else if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, { 8953 key: make_sequence(self, exprs), 8954 value: make_node(AST_Function, self, { 8955 argnames: [], 8956 body: [], 8957 }).init_vars(node), 8958 })); 8959 return node; 8960 } 8961 if (values) exprs.push(make_node(AST_Call, self, { 8962 expression: make_node(AST_Arrow, self, { 8963 argnames: [], 8964 body: [], 8965 value: make_value(), 8966 }).init_vars(self.parent_scope), 8967 args: [], 8968 })); 8969 return make_sequence(self, exprs); 8970 8971 function make_value() { 8972 return make_sequence(self, values.map(function(node) { 8973 if (!(node instanceof AST_ClassInit)) return node; 8974 var fn = make_node(AST_Arrow, node.value); 8975 fn.argnames = []; 8976 return make_node(AST_Call, node, { 8977 expression: fn, 8978 args: [], 8979 }); 8980 })); 8981 } 8982 }); 8983 def(AST_Conditional, function(compressor) { 8984 var consequent = this.consequent.drop_side_effect_free(compressor); 8985 var alternative = this.alternative.drop_side_effect_free(compressor); 8986 if (consequent === this.consequent && alternative === this.alternative) return this; 8987 var exprs; 8988 if (compressor.option("ie")) { 8989 exprs = []; 8990 if (consequent instanceof AST_Function) { 8991 exprs.push(consequent); 8992 consequent = null; 8993 } 8994 if (alternative instanceof AST_Function) { 8995 exprs.push(alternative); 8996 alternative = null; 8997 } 8998 } 8999 var node; 9000 if (!consequent) { 9001 node = alternative ? make_node(AST_Binary, this, { 9002 operator: "||", 9003 left: this.condition, 9004 right: alternative, 9005 }) : this.condition.drop_side_effect_free(compressor); 9006 } else if (!alternative) { 9007 node = make_node(AST_Binary, this, { 9008 operator: "&&", 9009 left: this.condition, 9010 right: consequent, 9011 }); 9012 } else { 9013 node = this.clone(); 9014 node.consequent = consequent; 9015 node.alternative = alternative; 9016 } 9017 if (!exprs) return node; 9018 if (node) exprs.push(node); 9019 return exprs.length == 0 ? null : make_sequence(this, exprs); 9020 }); 9021 def(AST_Constant, return_null); 9022 def(AST_Dot, function(compressor, first_in_statement) { 9023 var expr = this.expression; 9024 if (expr.may_throw_on_access(compressor)) return this; 9025 return expr.drop_side_effect_free(compressor, first_in_statement); 9026 }); 9027 def(AST_Function, function(compressor) { 9028 return fn_name_unused(this, compressor) ? null : this; 9029 }); 9030 def(AST_LambdaExpression, return_null); 9031 def(AST_Object, function(compressor, first_in_statement) { 9032 var exprs = []; 9033 this.properties.forEach(function(prop) { 9034 if (prop instanceof AST_Spread) { 9035 exprs.push(prop); 9036 } else { 9037 if (prop.key instanceof AST_Node) exprs.push(prop.key); 9038 exprs.push(prop.value); 9039 } 9040 }); 9041 var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) { 9042 var exp = node.expression; 9043 return exp.safe_to_spread() ? exp.drop_side_effect_free(compressor, first_in_statement) : node; 9044 }); 9045 if (!values) return null; 9046 if (values === exprs && !all(values, function(node) { 9047 return !(node instanceof AST_Spread); 9048 })) return this; 9049 return make_sequence(this, values.map(function(node) { 9050 return node instanceof AST_Spread ? make_node(AST_Object, node, { properties: [ node ] }) : node; 9051 })); 9052 }); 9053 def(AST_ObjectIdentity, return_null); 9054 def(AST_Sequence, function(compressor, first_in_statement) { 9055 var expressions = trim(this.expressions, compressor, first_in_statement); 9056 if (!expressions) return null; 9057 var end = expressions.length - 1; 9058 var last = expressions[end]; 9059 if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) { 9060 expressions = expressions.slice(0, -1); 9061 end--; 9062 var expr = expressions[end]; 9063 last.expression = needs_enqueuing(compressor, expr) ? expr : expr.negate(compressor); 9064 expressions[end] = last; 9065 } 9066 var assign, cond, lhs; 9067 if (compressor.option("conditionals") 9068 && end > 0 9069 && (assign = expressions[end - 1]) instanceof AST_Assign 9070 && assign.operator == "=" 9071 && (lhs = assign.left) instanceof AST_SymbolRef 9072 && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) { 9073 assign = assign.clone(); 9074 assign.right = cond; 9075 expressions = expressions.slice(0, -2); 9076 expressions.push(assign.drop_side_effect_free(compressor, first_in_statement)); 9077 } 9078 return expressions === this.expressions ? this : make_sequence(this, expressions); 9079 }); 9080 def(AST_Sub, function(compressor, first_in_statement) { 9081 var expr = this.expression; 9082 if (expr.may_throw_on_access(compressor)) return this; 9083 var prop = this.property; 9084 expr = expr.drop_side_effect_free(compressor, first_in_statement); 9085 if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement); 9086 prop = prop.drop_side_effect_free(compressor); 9087 if (!prop) return expr; 9088 return make_sequence(this, [ expr, prop ]); 9089 }); 9090 def(AST_SymbolRef, function(compressor) { 9091 return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this; 9092 }); 9093 def(AST_Template, function(compressor, first_in_statement) { 9094 var self = this; 9095 if (self.is_expr_pure(compressor)) { 9096 var expressions = self.expressions; 9097 if (expressions.length == 0) return null; 9098 return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement); 9099 } 9100 var tag = self.tag; 9101 var dropped = drop_returns(compressor, tag); 9102 if (dropped) { 9103 // always shallow clone to signal internal changes 9104 self = self.clone(); 9105 self.tag = dropped; 9106 // avoid extraneous traversal 9107 if (tag._squeezed) self.tag._squeezed = true; 9108 } 9109 return self; 9110 }); 9111 def(AST_Unary, function(compressor, first_in_statement) { 9112 var exp = this.expression; 9113 if (unary_side_effects[this.operator]) { 9114 this.write_only = !exp.has_side_effects(compressor); 9115 return this; 9116 } 9117 if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) { 9118 return null; 9119 } 9120 var node = exp.drop_side_effect_free(compressor, first_in_statement); 9121 if (first_in_statement && node && is_iife_call(node)) { 9122 if (node === exp && this.operator == "!") return this; 9123 return node.negate(compressor, first_in_statement); 9124 } 9125 return node; 9126 }); 9127 })(function(node, func) { 9128 node.DEFMETHOD("drop_side_effect_free", func); 9129 }); 9130 9131 OPT(AST_SimpleStatement, function(self, compressor) { 9132 if (compressor.option("side_effects")) { 9133 var body = self.body; 9134 var node = body.drop_side_effect_free(compressor, true); 9135 if (!node) { 9136 AST_Node.warn("Dropping side-effect-free statement [{start}]", self); 9137 return make_node(AST_EmptyStatement, self); 9138 } 9139 if (node !== body) { 9140 return make_node(AST_SimpleStatement, self, { body: node }); 9141 } 9142 } 9143 return self; 9144 }); 9145 9146 OPT(AST_While, function(self, compressor) { 9147 return compressor.option("loops") ? make_node(AST_For, self).optimize(compressor) : self; 9148 }); 9149 9150 function has_loop_control(loop, parent, type) { 9151 if (!type) type = AST_LoopControl; 9152 var found = false; 9153 var tw = new TreeWalker(function(node) { 9154 if (found || node instanceof AST_Scope) return true; 9155 if (node instanceof type && tw.loopcontrol_target(node) === loop) { 9156 return found = true; 9157 } 9158 }); 9159 if (parent instanceof AST_LabeledStatement) tw.push(parent); 9160 tw.push(loop); 9161 loop.body.walk(tw); 9162 return found; 9163 } 9164 9165 OPT(AST_Do, function(self, compressor) { 9166 if (!compressor.option("loops")) return self; 9167 var cond = fuzzy_eval(compressor, self.condition); 9168 if (!(cond instanceof AST_Node)) { 9169 if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, { 9170 body: make_node(AST_BlockStatement, self.body, { 9171 body: [ 9172 self.body, 9173 make_node(AST_SimpleStatement, self.condition, { body: self.condition }), 9174 ], 9175 }), 9176 }).optimize(compressor); 9177 if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, { 9178 body: [ 9179 self.body, 9180 make_node(AST_SimpleStatement, self.condition, { body: self.condition }), 9181 ], 9182 }).optimize(compressor); 9183 } 9184 if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) { 9185 var body = self.body.body; 9186 for (var i = body.length; --i >= 0;) { 9187 var stat = body[i]; 9188 if (stat instanceof AST_If 9189 && !stat.alternative 9190 && stat.body instanceof AST_Break 9191 && compressor.loopcontrol_target(stat.body) === self) { 9192 if (has_block_scope_refs(stat.condition)) break; 9193 self.condition = make_node(AST_Binary, self, { 9194 operator: "&&", 9195 left: stat.condition.negate(compressor), 9196 right: self.condition, 9197 }); 9198 body.splice(i, 1); 9199 } else if (stat instanceof AST_SimpleStatement) { 9200 if (has_block_scope_refs(stat.body)) break; 9201 self.condition = make_sequence(self, [ 9202 stat.body, 9203 self.condition, 9204 ]); 9205 body.splice(i, 1); 9206 } else if (!is_declaration(stat, true)) { 9207 break; 9208 } 9209 } 9210 self.body = trim_block(self.body, compressor.parent()); 9211 } 9212 if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self).optimize(compressor); 9213 if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, { 9214 condition: make_sequence(self.condition, [ 9215 self.body.body, 9216 self.condition, 9217 ]), 9218 body: make_node(AST_EmptyStatement, self), 9219 }).optimize(compressor); 9220 return self; 9221 9222 function has_block_scope_refs(node) { 9223 var found = false; 9224 node.walk(new TreeWalker(function(node) { 9225 if (found) return true; 9226 if (node instanceof AST_SymbolRef) { 9227 if (!member(node.definition(), self.enclosed)) found = true; 9228 return true; 9229 } 9230 })); 9231 return found; 9232 } 9233 }); 9234 9235 function if_break_in_loop(self, compressor) { 9236 var first = first_statement(self.body); 9237 if (compressor.option("dead_code") 9238 && (first instanceof AST_Break 9239 || first instanceof AST_Continue && external_target(first) 9240 || first instanceof AST_Exit)) { 9241 var body = []; 9242 if (is_statement(self.init)) { 9243 body.push(self.init); 9244 } else if (self.init) { 9245 body.push(make_node(AST_SimpleStatement, self.init, { body: self.init })); 9246 } 9247 var retain = external_target(first) || first instanceof AST_Exit; 9248 if (self.condition && retain) { 9249 body.push(make_node(AST_If, self, { 9250 condition: self.condition, 9251 body: first, 9252 alternative: null, 9253 })); 9254 } else if (self.condition) { 9255 body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition })); 9256 } else if (retain) { 9257 body.push(first); 9258 } 9259 extract_declarations_from_unreachable_code(compressor, self.body, body); 9260 return make_node(AST_BlockStatement, self, { body: body }); 9261 } 9262 if (first instanceof AST_If) { 9263 var ab = first_statement(first.body); 9264 if (ab instanceof AST_Break && !external_target(ab)) { 9265 if (self.condition) { 9266 self.condition = make_node(AST_Binary, self.condition, { 9267 left: self.condition, 9268 operator: "&&", 9269 right: first.condition.negate(compressor), 9270 }); 9271 } else { 9272 self.condition = first.condition.negate(compressor); 9273 } 9274 var body = as_statement_array(first.alternative); 9275 extract_declarations_from_unreachable_code(compressor, first.body, body); 9276 return drop_it(body); 9277 } 9278 ab = first_statement(first.alternative); 9279 if (ab instanceof AST_Break && !external_target(ab)) { 9280 if (self.condition) { 9281 self.condition = make_node(AST_Binary, self.condition, { 9282 left: self.condition, 9283 operator: "&&", 9284 right: first.condition, 9285 }); 9286 } else { 9287 self.condition = first.condition; 9288 } 9289 var body = as_statement_array(first.body); 9290 extract_declarations_from_unreachable_code(compressor, first.alternative, body); 9291 return drop_it(body); 9292 } 9293 } 9294 return self; 9295 9296 function first_statement(body) { 9297 return body instanceof AST_BlockStatement ? body.body[0] : body; 9298 } 9299 9300 function external_target(node) { 9301 return compressor.loopcontrol_target(node) !== compressor.self(); 9302 } 9303 9304 function drop_it(rest) { 9305 if (self.body instanceof AST_BlockStatement) { 9306 self.body = self.body.clone(); 9307 self.body.body = rest.concat(self.body.body.slice(1)); 9308 self.body = self.body.transform(compressor); 9309 } else { 9310 self.body = make_node(AST_BlockStatement, self.body, { body: rest }).transform(compressor); 9311 } 9312 return if_break_in_loop(self, compressor); 9313 } 9314 } 9315 9316 OPT(AST_For, function(self, compressor) { 9317 if (!compressor.option("loops")) return self; 9318 if (compressor.option("side_effects")) { 9319 if (self.init) self.init = self.init.drop_side_effect_free(compressor); 9320 if (self.step) self.step = self.step.drop_side_effect_free(compressor); 9321 } 9322 if (self.condition) { 9323 var cond = fuzzy_eval(compressor, self.condition); 9324 if (!cond) { 9325 if (compressor.option("dead_code")) { 9326 var body = []; 9327 if (is_statement(self.init)) { 9328 body.push(self.init); 9329 } else if (self.init) { 9330 body.push(make_node(AST_SimpleStatement, self.init, { body: self.init })); 9331 } 9332 body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition })); 9333 extract_declarations_from_unreachable_code(compressor, self.body, body); 9334 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); 9335 } 9336 } else if (!(cond instanceof AST_Node)) { 9337 self.body = make_node(AST_BlockStatement, self.body, { 9338 body: [ 9339 make_node(AST_SimpleStatement, self.condition, { body: self.condition }), 9340 self.body, 9341 ], 9342 }); 9343 self.condition = null; 9344 } 9345 } 9346 return if_break_in_loop(self, compressor); 9347 }); 9348 9349 OPT(AST_ForEnumeration, function(self, compressor) { 9350 if (compressor.option("varify") && is_lexical_definition(self.init)) { 9351 var name = self.init.definitions[0].name; 9352 if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet) 9353 && !name.match_symbol(function(node) { 9354 if (node instanceof AST_SymbolDeclaration) { 9355 var def = node.definition(); 9356 return !same_scope(def) || may_overlap(compressor, def); 9357 } 9358 }, true)) { 9359 self.init = to_var(self.init, self.resolve()); 9360 } 9361 } 9362 return self; 9363 }); 9364 9365 function mark_locally_defined(condition, consequent, alternative) { 9366 if (condition instanceof AST_Sequence) condition = condition.tail_node(); 9367 if (!(condition instanceof AST_Binary)) return; 9368 if (!(condition.left instanceof AST_String)) { 9369 switch (condition.operator) { 9370 case "&&": 9371 mark_locally_defined(condition.left, consequent); 9372 mark_locally_defined(condition.right, consequent); 9373 break; 9374 case "||": 9375 mark_locally_defined(negate(condition.left), alternative); 9376 mark_locally_defined(negate(condition.right), alternative); 9377 break; 9378 } 9379 return; 9380 } 9381 if (!(condition.right instanceof AST_UnaryPrefix)) return; 9382 if (condition.right.operator != "typeof") return; 9383 var sym = condition.right.expression; 9384 if (!is_undeclared_ref(sym)) return; 9385 var body; 9386 var undef = condition.left.value == "undefined"; 9387 switch (condition.operator) { 9388 case "==": 9389 body = undef ? alternative : consequent; 9390 break; 9391 case "!=": 9392 body = undef ? consequent : alternative; 9393 break; 9394 default: 9395 return; 9396 } 9397 if (!body) return; 9398 var abort = false; 9399 var def = sym.definition(); 9400 var fn; 9401 var refs = []; 9402 var scanned = []; 9403 var tw = new TreeWalker(function(node, descend) { 9404 if (abort) return true; 9405 if (node instanceof AST_Assign) { 9406 var ref = node.left; 9407 if (!(ref instanceof AST_SymbolRef && ref.definition() === def)) return; 9408 node.right.walk(tw); 9409 switch (node.operator) { 9410 case "=": 9411 case "&&=": 9412 abort = true; 9413 } 9414 return true; 9415 } 9416 if (node instanceof AST_Call) { 9417 descend(); 9418 fn = node.expression.tail_node(); 9419 var save; 9420 if (fn instanceof AST_SymbolRef) { 9421 fn = fn.fixed_value(); 9422 save = refs.length; 9423 } 9424 if (!(fn instanceof AST_Lambda)) { 9425 abort = true; 9426 } else if (push_uniq(scanned, fn)) { 9427 fn.walk(tw); 9428 } 9429 if (save >= 0) refs.length = save; 9430 return true; 9431 } 9432 if (node instanceof AST_DWLoop) { 9433 var save = refs.length; 9434 descend(); 9435 if (abort) refs.length = save; 9436 return true; 9437 } 9438 if (node instanceof AST_For) { 9439 if (node.init) node.init.walk(tw); 9440 var save = refs.length; 9441 if (node.condition) node.condition.walk(tw); 9442 node.body.walk(tw); 9443 if (node.step) node.step.walk(tw); 9444 if (abort) refs.length = save; 9445 return true; 9446 } 9447 if (node instanceof AST_ForEnumeration) { 9448 node.object.walk(tw); 9449 var save = refs.length; 9450 node.init.walk(tw); 9451 node.body.walk(tw); 9452 if (abort) refs.length = save; 9453 return true; 9454 } 9455 if (node instanceof AST_Scope) { 9456 if (node === fn) return; 9457 return true; 9458 } 9459 if (node instanceof AST_SymbolRef) { 9460 if (node.definition() === def) refs.push(node); 9461 return true; 9462 } 9463 }); 9464 body.walk(tw); 9465 refs.forEach(function(ref) { 9466 ref.defined = true; 9467 }); 9468 9469 function negate(node) { 9470 if (!(node instanceof AST_Binary)) return; 9471 switch (node.operator) { 9472 case "==": 9473 node = node.clone(); 9474 node.operator = "!="; 9475 return node; 9476 case "!=": 9477 node = node.clone(); 9478 node.operator = "=="; 9479 return node; 9480 } 9481 } 9482 } 9483 9484 function fuzzy_eval(compressor, node, nullish) { 9485 if (node.truthy) return true; 9486 if (is_undefined(node)) return undefined; 9487 if (node.falsy && !nullish) return false; 9488 if (node.is_truthy()) return true; 9489 return node.evaluate(compressor, true); 9490 } 9491 9492 function mark_duplicate_condition(compressor, node) { 9493 var child; 9494 var level = 0; 9495 var negated = false; 9496 var parent = compressor.self(); 9497 if (!is_statement(parent)) while (true) { 9498 child = parent; 9499 parent = compressor.parent(level++); 9500 if (parent instanceof AST_Binary) { 9501 switch (child) { 9502 case parent.left: 9503 if (lazy_op[parent.operator]) continue; 9504 break; 9505 case parent.right: 9506 if (match(parent.left)) switch (parent.operator) { 9507 case "&&": 9508 node[negated ? "falsy" : "truthy"] = true; 9509 break; 9510 case "||": 9511 case "??": 9512 node[negated ? "truthy" : "falsy"] = true; 9513 break; 9514 } 9515 break; 9516 } 9517 } else if (parent instanceof AST_Conditional) { 9518 var cond = parent.condition; 9519 if (cond === child) continue; 9520 if (match(cond)) switch (child) { 9521 case parent.consequent: 9522 node[negated ? "falsy" : "truthy"] = true; 9523 break; 9524 case parent.alternative: 9525 node[negated ? "truthy" : "falsy"] = true; 9526 break; 9527 } 9528 } else if (parent instanceof AST_Exit) { 9529 break; 9530 } else if (parent instanceof AST_If) { 9531 break; 9532 } else if (parent instanceof AST_Sequence) { 9533 if (parent.expressions[0] === child) continue; 9534 } else if (parent instanceof AST_SimpleStatement) { 9535 break; 9536 } 9537 return; 9538 } 9539 while (true) { 9540 child = parent; 9541 parent = compressor.parent(level++); 9542 if (parent instanceof AST_BlockStatement) { 9543 if (parent.body[0] === child) continue; 9544 } else if (parent instanceof AST_If) { 9545 if (match(parent.condition)) switch (child) { 9546 case parent.body: 9547 node[negated ? "falsy" : "truthy"] = true; 9548 break; 9549 case parent.alternative: 9550 node[negated ? "truthy" : "falsy"] = true; 9551 break; 9552 } 9553 } 9554 return; 9555 } 9556 9557 function match(cond) { 9558 if (node.equals(cond)) return true; 9559 if (!(cond instanceof AST_UnaryPrefix)) return false; 9560 if (cond.operator != "!") return false; 9561 if (!node.equals(cond.expression)) return false; 9562 negated = true; 9563 return true; 9564 } 9565 } 9566 9567 OPT(AST_If, function(self, compressor) { 9568 if (is_empty(self.alternative)) self.alternative = null; 9569 9570 if (!compressor.option("conditionals")) return self; 9571 if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) { 9572 mark_duplicate_condition(compressor, self.condition); 9573 } 9574 // if condition can be statically determined, warn and drop 9575 // one of the blocks. note, statically determined implies 9576 // “has no side effects”; also it doesn't work for cases like 9577 // `x && true`, though it probably should. 9578 if (compressor.option("dead_code")) { 9579 var cond = fuzzy_eval(compressor, self.condition); 9580 if (!cond) { 9581 AST_Node.warn("Condition always false [{start}]", self.condition); 9582 var body = [ 9583 make_node(AST_SimpleStatement, self.condition, { body: self.condition }).transform(compressor), 9584 ]; 9585 extract_declarations_from_unreachable_code(compressor, self.body, body); 9586 if (self.alternative) body.push(self.alternative); 9587 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); 9588 } else if (!(cond instanceof AST_Node)) { 9589 AST_Node.warn("Condition always true [{start}]", self.condition); 9590 var body = [ 9591 make_node(AST_SimpleStatement, self.condition, { body: self.condition }).transform(compressor), 9592 self.body, 9593 ]; 9594 if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body); 9595 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); 9596 } 9597 } 9598 var negated = self.condition.negate(compressor); 9599 var self_condition_length = self.condition.print_to_string().length; 9600 var negated_length = negated.print_to_string().length; 9601 var negated_is_best = negated_length < self_condition_length; 9602 if (self.alternative && negated_is_best) { 9603 negated_is_best = false; // because we already do the switch here. 9604 // no need to swap values of self_condition_length and negated_length 9605 // here because they are only used in an equality comparison later on. 9606 self.condition = negated; 9607 var tmp = self.body; 9608 self.body = self.alternative; 9609 self.alternative = is_empty(tmp) ? null : tmp; 9610 } 9611 var body_defuns = []; 9612 var body_var_defs = []; 9613 var body_refs = []; 9614 var body_exprs = sequencesize(self.body, body_defuns, body_var_defs, body_refs); 9615 var alt_defuns = []; 9616 var alt_var_defs = []; 9617 var alt_refs = []; 9618 var alt_exprs = sequencesize(self.alternative, alt_defuns, alt_var_defs, alt_refs); 9619 if (body_exprs instanceof AST_BlockStatement || alt_exprs instanceof AST_BlockStatement) { 9620 var body = [], var_defs = []; 9621 if (body_exprs) { 9622 [].push.apply(body, body_defuns); 9623 [].push.apply(var_defs, body_var_defs); 9624 if (body_exprs instanceof AST_BlockStatement) { 9625 self.body = body_exprs; 9626 } else if (body_exprs.length == 0) { 9627 self.body = make_node(AST_EmptyStatement, self.body); 9628 } else { 9629 self.body = make_node(AST_SimpleStatement, self.body, { 9630 body: make_sequence(self.body, body_exprs), 9631 }); 9632 } 9633 body_refs.forEach(process_to_assign); 9634 } 9635 if (alt_exprs) { 9636 [].push.apply(body, alt_defuns); 9637 [].push.apply(var_defs, alt_var_defs); 9638 if (alt_exprs instanceof AST_BlockStatement) { 9639 self.alternative = alt_exprs; 9640 } else if (alt_exprs.length == 0) { 9641 self.alternative = null; 9642 } else { 9643 self.alternative = make_node(AST_SimpleStatement, self.alternative, { 9644 body: make_sequence(self.alternative, alt_exprs), 9645 }); 9646 } 9647 alt_refs.forEach(process_to_assign); 9648 } 9649 if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs })); 9650 if (body.length > 0) { 9651 body.push(self.transform(compressor)); 9652 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); 9653 } 9654 } else if (body_exprs && alt_exprs) { 9655 var body = body_defuns.concat(alt_defuns); 9656 if (body_var_defs.length > 0 || alt_var_defs.length > 0) body.push(make_node(AST_Var, self, { 9657 definitions: body_var_defs.concat(alt_var_defs), 9658 })); 9659 if (body_exprs.length == 0) { 9660 body.push(make_node(AST_SimpleStatement, self.condition, { 9661 body: alt_exprs.length > 0 ? make_node(AST_Binary, self, { 9662 operator: "||", 9663 left: self.condition, 9664 right: make_sequence(self.alternative, alt_exprs), 9665 }).transform(compressor) : self.condition.clone(), 9666 }).optimize(compressor)); 9667 } else if (alt_exprs.length == 0) { 9668 if (self_condition_length === negated_length && !negated_is_best 9669 && self.condition instanceof AST_Binary && self.condition.operator == "||") { 9670 // although the code length of self.condition and negated are the same, 9671 // negated does not require additional surrounding parentheses. 9672 // see https://github.com/mishoo/UglifyJS/issues/979 9673 negated_is_best = true; 9674 } 9675 body.push(make_node(AST_SimpleStatement, self, { 9676 body: make_node(AST_Binary, self, { 9677 operator: negated_is_best ? "||" : "&&", 9678 left: negated_is_best ? negated : self.condition, 9679 right: make_sequence(self.body, body_exprs), 9680 }).transform(compressor), 9681 }).optimize(compressor)); 9682 } else { 9683 body.push(make_node(AST_SimpleStatement, self, { 9684 body: make_node(AST_Conditional, self, { 9685 condition: self.condition, 9686 consequent: make_sequence(self.body, body_exprs), 9687 alternative: make_sequence(self.alternative, alt_exprs), 9688 }), 9689 }).optimize(compressor)); 9690 } 9691 body_refs.forEach(process_to_assign); 9692 alt_refs.forEach(process_to_assign); 9693 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); 9694 } 9695 if (is_empty(self.body)) self = make_node(AST_If, self, { 9696 condition: negated, 9697 body: self.alternative, 9698 alternative: null, 9699 }); 9700 if (self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) { 9701 var cons_value = self.body.value; 9702 var alt_value = self.alternative.value; 9703 if (!cons_value && !alt_value) return make_node(AST_BlockStatement, self, { 9704 body: [ 9705 make_node(AST_SimpleStatement, self, { body: self.condition }), 9706 self.body, 9707 ], 9708 }).optimize(compressor); 9709 if (cons_value && alt_value || !keep_return_void()) { 9710 var exit = make_node(self.body.CTOR, self, { 9711 value: make_node(AST_Conditional, self, { 9712 condition: self.condition, 9713 consequent: cons_value || make_node(AST_Undefined, self.body).transform(compressor), 9714 alternative: alt_value || make_node(AST_Undefined, self.alternative).transform(compressor), 9715 }), 9716 }); 9717 if (exit instanceof AST_Return) exit.in_bool = self.body.in_bool || self.alternative.in_bool; 9718 return exit; 9719 } 9720 } 9721 if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) { 9722 self = make_node(AST_If, self, { 9723 condition: make_node(AST_Binary, self.condition, { 9724 operator: "&&", 9725 left: self.condition, 9726 right: self.body.condition, 9727 }), 9728 body: self.body.body, 9729 alternative: null, 9730 }); 9731 } 9732 if (aborts(self.body) && self.alternative) { 9733 var alt = self.alternative; 9734 self.alternative = null; 9735 return make_node(AST_BlockStatement, self, { body: [ self, alt ] }).optimize(compressor); 9736 } 9737 if (aborts(self.alternative)) { 9738 var body = self.body; 9739 self.body = self.alternative; 9740 self.condition = negated_is_best ? negated : self.condition.negate(compressor); 9741 self.alternative = null; 9742 return make_node(AST_BlockStatement, self, { body: [ self, body ] }).optimize(compressor); 9743 } 9744 if (self.alternative) { 9745 var body_stats = as_array(self.body); 9746 var body_index = last_index(body_stats); 9747 var alt_stats = as_array(self.alternative); 9748 var alt_index = last_index(alt_stats); 9749 for (var stats = []; body_index >= 0 && alt_index >= 0;) { 9750 var stat = body_stats[body_index]; 9751 var alt_stat = alt_stats[alt_index]; 9752 if (stat.equals(alt_stat)) { 9753 body_stats.splice(body_index--, 1); 9754 alt_stats.splice(alt_index--, 1); 9755 stats.unshift(merge_expression(stat, alt_stat)); 9756 } else { 9757 if (!(stat instanceof AST_SimpleStatement)) break; 9758 if (!(alt_stat instanceof AST_SimpleStatement)) break; 9759 var expr1 = stat.body.tail_node(); 9760 var expr2 = alt_stat.body.tail_node(); 9761 if (!expr1.equals(expr2)) break; 9762 body_index = pop_expr(body_stats, stat.body, body_index); 9763 alt_index = pop_expr(alt_stats, alt_stat.body, alt_index); 9764 stats.unshift(make_node(AST_SimpleStatement, expr1, { body: merge_expression(expr1, expr2) })); 9765 } 9766 } 9767 if (stats.length > 0) { 9768 self.body = body_stats.length > 0 ? make_node(AST_BlockStatement, self, { 9769 body: body_stats, 9770 }) : make_node(AST_EmptyStatement, self); 9771 self.alternative = alt_stats.length > 0 ? make_node(AST_BlockStatement, self, { 9772 body: alt_stats, 9773 }) : null; 9774 stats.unshift(self); 9775 return make_node(AST_BlockStatement, self, { body: stats }).optimize(compressor); 9776 } 9777 } 9778 if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative); 9779 return self; 9780 9781 function as_array(node) { 9782 return node instanceof AST_BlockStatement ? node.body : [ node ]; 9783 } 9784 9785 function keep_return_void() { 9786 var has_finally = false, level = 0, node = compressor.self(); 9787 do { 9788 if (node instanceof AST_Catch) { 9789 if (compressor.parent(level).bfinally) has_finally = true; 9790 level++; 9791 } else if (node instanceof AST_Finally) { 9792 level++; 9793 } else if (node instanceof AST_Scope) { 9794 return has_finally && in_async_generator(node); 9795 } else if (node instanceof AST_Try) { 9796 if (node.bfinally) has_finally = true; 9797 } 9798 } while (node = compressor.parent(level++)); 9799 } 9800 9801 function last_index(stats) { 9802 for (var index = stats.length; --index >= 0;) { 9803 if (!is_declaration(stats[index], true)) break; 9804 } 9805 return index; 9806 } 9807 9808 function pop_expr(stats, body, index) { 9809 if (body instanceof AST_Sequence) { 9810 stats[index] = make_node(AST_SimpleStatement, body, { 9811 body: make_sequence(body, body.expressions.slice(0, -1)), 9812 }); 9813 } else { 9814 stats.splice(index--, 1); 9815 } 9816 return index; 9817 } 9818 9819 function sequencesize(stat, defuns, var_defs, refs) { 9820 if (stat == null) return []; 9821 if (stat instanceof AST_BlockStatement) { 9822 var exprs = []; 9823 for (var i = 0; i < stat.body.length; i++) { 9824 var line = stat.body[i]; 9825 if (line instanceof AST_EmptyStatement) continue; 9826 if (line instanceof AST_Exit) { 9827 if (i == 0) return; 9828 if (exprs.length > 0) { 9829 line = line.clone(); 9830 exprs.push(line.value || make_node(AST_Undefined, line).transform(compressor)); 9831 line.value = make_sequence(stat, exprs); 9832 } 9833 var block = stat.clone(); 9834 block.body = block.body.slice(i + 1); 9835 block.body.unshift(line); 9836 return block; 9837 } 9838 if (line instanceof AST_LambdaDefinition) { 9839 defuns.push(line); 9840 } else if (line instanceof AST_SimpleStatement) { 9841 if (!compressor.option("sequences") && exprs.length > 0) return; 9842 exprs.push(line.body); 9843 } else if (line instanceof AST_Var) { 9844 if (!compressor.option("sequences") && exprs.length > 0) return; 9845 line.remove_initializers(compressor, var_defs); 9846 line.definitions.forEach(process_var_def); 9847 } else { 9848 return; 9849 } 9850 } 9851 return exprs; 9852 } 9853 if (stat instanceof AST_LambdaDefinition) { 9854 defuns.push(stat); 9855 return []; 9856 } 9857 if (stat instanceof AST_EmptyStatement) return []; 9858 if (stat instanceof AST_SimpleStatement) return [ stat.body ]; 9859 if (stat instanceof AST_Var) { 9860 var exprs = []; 9861 stat.remove_initializers(compressor, var_defs); 9862 stat.definitions.forEach(process_var_def); 9863 return exprs; 9864 } 9865 9866 function process_var_def(var_def) { 9867 if (!var_def.value) return; 9868 exprs.push(make_node(AST_Assign, var_def, { 9869 operator: "=", 9870 left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) { 9871 refs.push(ref); 9872 }), 9873 right: var_def.value, 9874 })); 9875 } 9876 } 9877 }); 9878 9879 OPT(AST_Switch, function(self, compressor) { 9880 if (!compressor.option("switches")) return self; 9881 if (!compressor.option("dead_code")) return self; 9882 var body = []; 9883 var branch; 9884 var decl = []; 9885 var default_branch; 9886 var exact_match; 9887 var side_effects = []; 9888 for (var i = 0, len = self.body.length; i < len; i++) { 9889 branch = self.body[i]; 9890 if (branch instanceof AST_Default) { 9891 var prev = body[body.length - 1]; 9892 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) { 9893 eliminate_branch(branch, prev); 9894 continue; 9895 } else { 9896 default_branch = branch; 9897 } 9898 } else { 9899 var exp = branch.expression; 9900 var equals = make_node(AST_Binary, self, { 9901 operator: "===", 9902 left: self.expression, 9903 right: exp, 9904 }).evaluate(compressor, true); 9905 if (!equals) { 9906 if (exp.has_side_effects(compressor)) side_effects.push(exp); 9907 eliminate_branch(branch, body[body.length - 1]); 9908 continue; 9909 } 9910 if (!(equals instanceof AST_Node)) { 9911 if (default_branch) { 9912 var default_index = body.indexOf(default_branch); 9913 body.splice(default_index, 1); 9914 eliminate_branch(default_branch, body[default_index - 1]); 9915 default_branch = null; 9916 } 9917 if (exp.has_side_effects(compressor)) { 9918 exact_match = branch; 9919 } else { 9920 default_branch = branch = make_node(AST_Default, branch); 9921 } 9922 while (++i < len) eliminate_branch(self.body[i], branch); 9923 } 9924 } 9925 if (i + 1 >= len || aborts(branch)) { 9926 var prev = body[body.length - 1]; 9927 var statements = branch.body; 9928 if (aborts(prev)) switch (prev.body.length - statements.length) { 9929 case 1: 9930 var stat = prev.body[prev.body.length - 1]; 9931 if (!is_break(stat, compressor)) break; 9932 statements = statements.concat(stat); 9933 case 0: 9934 var prev_block = make_node(AST_BlockStatement, prev); 9935 var next_block = make_node(AST_BlockStatement, branch, { body: statements }); 9936 if (prev_block.equals(next_block)) prev.body = []; 9937 } 9938 } 9939 if (side_effects.length) { 9940 if (branch instanceof AST_Default) { 9941 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] })); 9942 } else { 9943 side_effects.push(branch.expression); 9944 branch.expression = make_sequence(self, side_effects); 9945 } 9946 side_effects = []; 9947 } 9948 body.push(branch); 9949 } 9950 if (side_effects.length && !exact_match) { 9951 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] })); 9952 } 9953 while (branch = body[body.length - 1]) { 9954 var stat = branch.body[branch.body.length - 1]; 9955 if (is_break(stat, compressor)) branch.body.pop(); 9956 if (branch === default_branch) { 9957 if (!has_declarations_only(branch)) break; 9958 } else if (branch.expression.has_side_effects(compressor)) { 9959 break; 9960 } else if (default_branch) { 9961 if (!has_declarations_only(default_branch)) break; 9962 if (body[body.length - 2] !== default_branch) break; 9963 default_branch.body = default_branch.body.concat(branch.body); 9964 branch.body = []; 9965 } else if (!has_declarations_only(branch)) break; 9966 eliminate_branch(branch); 9967 if (body.pop() === default_branch) default_branch = null; 9968 } 9969 if (!branch) { 9970 decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression })); 9971 if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, { 9972 body: make_sequence(self, side_effects), 9973 })); 9974 return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor); 9975 } 9976 if (branch === default_branch) while (branch = body[body.length - 2]) { 9977 if (branch instanceof AST_Default) break; 9978 if (!has_declarations_only(branch)) break; 9979 var exp = branch.expression; 9980 if (exp.has_side_effects(compressor)) { 9981 var prev = body[body.length - 3]; 9982 if (prev && !aborts(prev)) break; 9983 default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp })); 9984 } 9985 eliminate_branch(branch); 9986 body.splice(-2, 1); 9987 } 9988 body[0].body = decl.concat(body[0].body); 9989 self.body = body; 9990 if (compressor.option("conditionals")) switch (body.length) { 9991 case 1: 9992 if (!no_break(body[0])) break; 9993 var exp = body[0].expression; 9994 var statements = body[0].body.slice(); 9995 if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, { 9996 condition: make_node(AST_Binary, self, { 9997 operator: "===", 9998 left: self.expression, 9999 right: exp, 10000 }), 10001 body: make_node(AST_BlockStatement, self, { body: statements }), 10002 alternative: null, 10003 }).optimize(compressor); 10004 if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, { body: exp })); 10005 statements.unshift(make_node(AST_SimpleStatement, self.expression, { body: self.expression })); 10006 return make_node(AST_BlockStatement, self, { body: statements }).optimize(compressor); 10007 case 2: 10008 if (!member(default_branch, body) || !no_break(body[1])) break; 10009 var statements = body[0].body.slice(); 10010 var exclusive = statements.length && is_break(statements[statements.length - 1], compressor); 10011 if (exclusive) statements.pop(); 10012 if (!all(statements, no_break)) break; 10013 var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1]); 10014 var node = make_node(AST_If, self, { 10015 condition: make_node(AST_Binary, self, body[0] === default_branch ? { 10016 operator: "!==", 10017 left: self.expression, 10018 right: body[1].expression, 10019 } : { 10020 operator: "===", 10021 left: self.expression, 10022 right: body[0].expression, 10023 }), 10024 body: make_node(AST_BlockStatement, body[0], { body: statements }), 10025 alternative: exclusive && alternative || null, 10026 }); 10027 if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, { body: [ node, alternative ] }); 10028 return node.optimize(compressor); 10029 } 10030 return self; 10031 10032 function is_break(node, tw) { 10033 return node instanceof AST_Break && tw.loopcontrol_target(node) === self; 10034 } 10035 10036 function no_break(node) { 10037 var found = false; 10038 var tw = new TreeWalker(function(node) { 10039 if (found 10040 || node instanceof AST_Lambda 10041 || node instanceof AST_SimpleStatement) return true; 10042 if (is_break(node, tw)) found = true; 10043 }); 10044 tw.push(self); 10045 node.walk(tw); 10046 return !found; 10047 } 10048 10049 function eliminate_branch(branch, prev) { 10050 if (prev && !aborts(prev)) { 10051 prev.body = prev.body.concat(branch.body); 10052 } else { 10053 extract_declarations_from_unreachable_code(compressor, branch, decl); 10054 } 10055 } 10056 }); 10057 10058 OPT(AST_Try, function(self, compressor) { 10059 self.body = tighten_body(self.body, compressor); 10060 if (compressor.option("dead_code")) { 10061 if (has_declarations_only(self) 10062 && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) { 10063 return node instanceof AST_SymbolCatch && !can_drop_symbol(node); 10064 }, true))) { 10065 var body = []; 10066 if (self.bcatch) { 10067 extract_declarations_from_unreachable_code(compressor, self.bcatch, body); 10068 body.forEach(function(stat) { 10069 if (!(stat instanceof AST_Var)) return; 10070 stat.definitions.forEach(function(var_def) { 10071 var def = var_def.name.definition().redefined(); 10072 if (!def) return; 10073 var_def.name = var_def.name.clone(); 10074 var_def.name.thedef = def; 10075 }); 10076 }); 10077 } 10078 body.unshift(make_node(AST_BlockStatement, self).optimize(compressor)); 10079 if (self.bfinally) { 10080 body.push(make_node(AST_BlockStatement, self.bfinally).optimize(compressor)); 10081 } 10082 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); 10083 } 10084 if (self.bfinally && has_declarations_only(self.bfinally)) { 10085 var body = make_node(AST_BlockStatement, self.bfinally).optimize(compressor); 10086 body = self.body.concat(body); 10087 if (!self.bcatch) return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); 10088 self.body = body; 10089 self.bfinally = null; 10090 } 10091 } 10092 return self; 10093 }); 10094 10095 function remove_initializers(make_value) { 10096 return function(compressor, defns) { 10097 var dropped = false; 10098 this.definitions.forEach(function(defn) { 10099 if (defn.value) dropped = true; 10100 defn.name.match_symbol(function(node) { 10101 if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, { 10102 name: node, 10103 value: make_value(compressor, node), 10104 })); 10105 }, true); 10106 }); 10107 return dropped; 10108 }; 10109 } 10110 10111 AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) { 10112 return make_node(AST_Undefined, node).optimize(compressor); 10113 })); 10114 AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null)); 10115 AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null)); 10116 10117 AST_Definitions.DEFMETHOD("to_assignments", function() { 10118 var assignments = this.definitions.reduce(function(a, defn) { 10119 var def = defn.name.definition(); 10120 var value = defn.value; 10121 if (value) { 10122 if (value instanceof AST_Sequence) value = value.clone(); 10123 var name = make_node(AST_SymbolRef, defn.name); 10124 var assign = make_node(AST_Assign, defn, { 10125 operator: "=", 10126 left: name, 10127 right: value, 10128 }); 10129 a.push(assign); 10130 var fixed = function() { 10131 return assign.right; 10132 }; 10133 fixed.assigns = [ assign ]; 10134 fixed.direct_access = def.direct_access; 10135 fixed.escaped = def.escaped; 10136 name.fixed = fixed; 10137 def.references.forEach(function(ref) { 10138 if (!ref.fixed) return; 10139 var assigns = ref.fixed.assigns; 10140 if (!assigns) return; 10141 if (assigns[0] !== defn) return; 10142 if (assigns.length > 1 || ref.fixed.to_binary || ref.fixed.to_prefix) { 10143 assigns[0] = assign; 10144 } else { 10145 ref.fixed = fixed; 10146 if (def.fixed === ref.fixed) def.fixed = fixed; 10147 } 10148 }); 10149 def.references.push(name); 10150 } 10151 def.assignments++; 10152 def.eliminated++; 10153 def.single_use = false; 10154 return a; 10155 }, []); 10156 if (assignments.length == 0) return null; 10157 return make_sequence(this, assignments); 10158 }); 10159 10160 function is_safe_lexical(def) { 10161 return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2); 10162 } 10163 10164 function may_overlap(compressor, def) { 10165 if (compressor.exposed(def)) return true; 10166 var scope = def.scope.resolve(); 10167 for (var s = def.scope; s !== scope;) { 10168 s = s.parent_scope; 10169 if (s.var_names().has(def.name)) return true; 10170 } 10171 } 10172 10173 function to_var(stat, scope) { 10174 return make_node(AST_Var, stat, { 10175 definitions: stat.definitions.map(function(defn) { 10176 return make_node(AST_VarDef, defn, { 10177 name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) { 10178 var def = name.definition(); 10179 def.orig[def.orig.indexOf(node)] = name; 10180 if (def.scope === scope) return; 10181 def.scope = scope; 10182 scope.variables.set(def.name, def); 10183 scope.enclosed.push(def); 10184 scope.var_names().set(def.name, true); 10185 }), 10186 value: defn.value, 10187 }); 10188 }), 10189 }); 10190 } 10191 10192 function can_varify(compressor, sym) { 10193 var def = sym.definition(); 10194 return (def.fixed || def.fixed === 0) 10195 && is_safe_lexical(def) 10196 && same_scope(def) 10197 && !may_overlap(compressor, def); 10198 } 10199 10200 function varify(self, compressor) { 10201 return compressor.option("varify") && all(self.definitions, function(defn) { 10202 return !defn.name.match_symbol(function(node) { 10203 if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node); 10204 }, true); 10205 }) ? to_var(self, compressor.find_parent(AST_Scope)) : self; 10206 } 10207 10208 OPT(AST_Const, varify); 10209 OPT(AST_Let, varify); 10210 10211 function trim_optional_chain(node, compressor) { 10212 if (!compressor.option("optional_chains")) return; 10213 if (node.terminal) do { 10214 var expr = node.expression; 10215 if (node.optional) { 10216 var ev = fuzzy_eval(compressor, expr, true); 10217 if (ev == null) return make_node(AST_UnaryPrefix, node, { 10218 operator: "void", 10219 expression: expr, 10220 }).optimize(compressor); 10221 if (!(ev instanceof AST_Node)) node.optional = false; 10222 } 10223 node = expr; 10224 } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal); 10225 } 10226 10227 function lift_sequence_in_expression(node, compressor) { 10228 var exp = node.expression; 10229 if (!(exp instanceof AST_Sequence)) return node; 10230 var x = exp.expressions.slice(); 10231 var e = node.clone(); 10232 e.expression = x.pop(); 10233 x.push(e); 10234 return make_sequence(node, x); 10235 } 10236 10237 function drop_unused_call_args(call, compressor, fns_with_marked_args) { 10238 var exp = call.expression; 10239 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp; 10240 if (!(fn instanceof AST_Lambda)) return; 10241 if (fn.uses_arguments) return; 10242 if (fn.pinned()) return; 10243 if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return; 10244 var args = call.args; 10245 if (!all(args, function(arg) { 10246 return !(arg instanceof AST_Spread); 10247 })) return; 10248 var argnames = fn.argnames; 10249 var is_iife = fn === exp && !fn.name; 10250 if (fn.rest) { 10251 if (!(is_iife && compressor.option("rests"))) return; 10252 var insert = argnames.length; 10253 args = args.slice(0, insert); 10254 while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor)); 10255 args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) })); 10256 argnames = argnames.concat(fn.rest); 10257 fn.rest = null; 10258 } else { 10259 args = args.slice(); 10260 argnames = argnames.slice(); 10261 } 10262 var pos = 0, last = 0; 10263 var drop_defaults = is_iife && compressor.option("default_values"); 10264 var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) { 10265 if (!argname) return true; 10266 if (argname instanceof AST_DestructuredArray) { 10267 return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array; 10268 } 10269 if (argname instanceof AST_DestructuredObject) { 10270 return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor); 10271 } 10272 return argname.unused; 10273 } : return_false; 10274 var side_effects = []; 10275 for (var i = 0; i < args.length; i++) { 10276 var argname = argnames[i]; 10277 if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) { 10278 argnames[i] = argname = argname.name; 10279 } 10280 if (!argname || argname.unused !== undefined) { 10281 var node = args[i].drop_side_effect_free(compressor); 10282 if (drop_fargs(argname)) { 10283 if (argname) argnames.splice(i, 1); 10284 args.splice(i, 1); 10285 if (node) side_effects.push(node); 10286 i--; 10287 continue; 10288 } else if (node) { 10289 side_effects.push(node); 10290 args[pos++] = make_sequence(call, side_effects); 10291 side_effects = []; 10292 } else if (argname) { 10293 if (side_effects.length) { 10294 args[pos++] = make_sequence(call, side_effects); 10295 side_effects = []; 10296 } else { 10297 args[pos++] = make_node(AST_Number, args[i], { value: 0 }); 10298 continue; 10299 } 10300 } 10301 } else if (drop_fargs(argname, args[i])) { 10302 var node = args[i].drop_side_effect_free(compressor); 10303 argnames.splice(i, 1); 10304 args.splice(i, 1); 10305 if (node) side_effects.push(node); 10306 i--; 10307 continue; 10308 } else { 10309 side_effects.push(args[i]); 10310 args[pos++] = make_sequence(call, side_effects); 10311 side_effects = []; 10312 } 10313 last = pos; 10314 } 10315 for (; i < argnames.length; i++) { 10316 if (drop_fargs(argnames[i])) argnames.splice(i--, 1); 10317 } 10318 fn.argnames = argnames; 10319 args.length = last; 10320 call.args = args; 10321 if (!side_effects.length) return; 10322 var arg = make_sequence(call, side_effects); 10323 args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, { 10324 operator: "void", 10325 expression: arg, 10326 }) : arg); 10327 } 10328 10329 function avoid_await_yield(compressor, parent_scope) { 10330 if (!parent_scope) parent_scope = compressor.find_parent(AST_Scope); 10331 var avoid = []; 10332 if (is_async(parent_scope) || parent_scope instanceof AST_Toplevel && compressor.option("module")) { 10333 avoid.push("await"); 10334 } 10335 if (is_generator(parent_scope)) avoid.push("yield"); 10336 return avoid.length && makePredicate(avoid); 10337 } 10338 10339 function safe_from_await_yield(fn, avoid) { 10340 if (!avoid) return true; 10341 var safe = true; 10342 var tw = new TreeWalker(function(node) { 10343 if (!safe) return true; 10344 if (node instanceof AST_Scope) { 10345 if (node === fn) return; 10346 if (is_arrow(node)) { 10347 for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw); 10348 } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) { 10349 safe = false; 10350 } 10351 return true; 10352 } 10353 if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false; 10354 }); 10355 fn.walk(tw); 10356 return safe; 10357 } 10358 10359 function safe_from_strict_mode(fn, compressor) { 10360 return fn.in_strict_mode(compressor) || !compressor.has_directive("use strict"); 10361 } 10362 10363 OPT(AST_Call, function(self, compressor) { 10364 var exp = self.expression; 10365 var terminated = trim_optional_chain(self, compressor); 10366 if (terminated) return terminated; 10367 if (compressor.option("sequences")) { 10368 if (exp instanceof AST_PropAccess) { 10369 var seq = lift_sequence_in_expression(exp, compressor); 10370 if (seq !== exp) { 10371 var call = self.clone(); 10372 call.expression = seq.expressions.pop(); 10373 seq.expressions.push(call); 10374 return seq.optimize(compressor); 10375 } 10376 } else if (!needs_unbinding(exp.tail_node())) { 10377 var seq = lift_sequence_in_expression(self, compressor); 10378 if (seq !== self) return seq.optimize(compressor); 10379 } 10380 } 10381 if (compressor.option("unused")) drop_unused_call_args(self, compressor); 10382 if (compressor.option("unsafe")) { 10383 if (is_undeclared_ref(exp)) switch (exp.name) { 10384 case "Array": 10385 // Array(n) ---> [ , , ... , ] 10386 if (self.args.length == 1) { 10387 var first = self.args[0]; 10388 if (first instanceof AST_Number) try { 10389 var length = first.value; 10390 if (length > 6) break; 10391 var elements = Array(length); 10392 for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self); 10393 return make_node(AST_Array, self, { elements: elements }); 10394 } catch (ex) { 10395 AST_Node.warn("Invalid array length: {length} [{start}]", { 10396 length: length, 10397 start: self.start, 10398 }); 10399 break; 10400 } 10401 if (!first.is_boolean(compressor) && !first.is_string(compressor)) break; 10402 } 10403 // Array(...) ---> [ ... ] 10404 return make_node(AST_Array, self, { elements: self.args }); 10405 case "Object": 10406 // Object() ---> {} 10407 if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] }); 10408 break; 10409 case "String": 10410 // String() ---> "" 10411 if (self.args.length == 0) return make_node(AST_String, self, { value: "" }); 10412 // String(x) ---> "" + x 10413 if (self.args.length == 1) return make_node(AST_Binary, self, { 10414 operator: "+", 10415 left: make_node(AST_String, self, { value: "" }), 10416 right: self.args[0], 10417 }).optimize(compressor); 10418 break; 10419 case "Number": 10420 // Number() ---> 0 10421 if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 }); 10422 // Number(x) ---> +("" + x) 10423 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { 10424 operator: "+", 10425 expression: make_node(AST_Binary, self, { 10426 operator: "+", 10427 left: make_node(AST_String, self, { value: "" }), 10428 right: self.args[0], 10429 }), 10430 }).optimize(compressor); 10431 break; 10432 case "Boolean": 10433 // Boolean() ---> false 10434 if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor); 10435 // Boolean(x) ---> !!x 10436 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { 10437 operator: "!", 10438 expression: make_node(AST_UnaryPrefix, self, { 10439 operator: "!", 10440 expression: self.args[0], 10441 }), 10442 }).optimize(compressor); 10443 break; 10444 case "RegExp": 10445 // attempt to convert RegExp(...) to literal 10446 var params = []; 10447 if (all(self.args, function(arg) { 10448 var value = arg.evaluate(compressor); 10449 params.unshift(value); 10450 return arg !== value; 10451 })) try { 10452 return best_of(compressor, self, make_node(AST_RegExp, self, { 10453 value: RegExp.apply(RegExp, params), 10454 })); 10455 } catch (ex) { 10456 AST_Node.warn("Error converting {this} [{start}]", self); 10457 } 10458 break; 10459 } else if (exp instanceof AST_Dot) switch (exp.property) { 10460 case "toString": 10461 // x.toString() ---> "" + x 10462 var expr = exp.expression; 10463 if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) { 10464 return make_node(AST_Binary, self, { 10465 operator: "+", 10466 left: make_node(AST_String, self, { value: "" }), 10467 right: expr, 10468 }).optimize(compressor); 10469 } 10470 break; 10471 case "join": 10472 if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: { 10473 var separator = self.args[0]; 10474 // [].join() ---> "" 10475 // [].join(x) ---> (x, "") 10476 if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) { 10477 return separator ? make_sequence(self, [ 10478 separator, 10479 make_node(AST_String, self, { value: "" }), 10480 ]).optimize(compressor) : make_node(AST_String, self, { value: "" }); 10481 } 10482 if (separator) { 10483 separator = separator.evaluate(compressor); 10484 if (separator instanceof AST_Node) break EXIT; // not a constant 10485 } 10486 var elements = []; 10487 var consts = []; 10488 for (var i = 0; i < exp.expression.elements.length; i++) { 10489 var el = exp.expression.elements[i]; 10490 var value = el.evaluate(compressor); 10491 if (value !== el) { 10492 consts.push(value); 10493 } else if (el instanceof AST_Spread) { 10494 break EXIT; 10495 } else { 10496 if (consts.length > 0) { 10497 elements.push(make_node(AST_String, self, { value: consts.join(separator) })); 10498 consts.length = 0; 10499 } 10500 elements.push(el); 10501 } 10502 } 10503 if (consts.length > 0) elements.push(make_node(AST_String, self, { 10504 value: consts.join(separator), 10505 })); 10506 // [ x ].join() ---> "" + x 10507 // [ x ].join(".") ---> "" + x 10508 // [ 1, 2, 3 ].join() ---> "1,2,3" 10509 // [ 1, 2, 3 ].join(".") ---> "1.2.3" 10510 if (elements.length == 1) { 10511 if (elements[0].is_string(compressor)) return elements[0]; 10512 return make_node(AST_Binary, elements[0], { 10513 operator: "+", 10514 left: make_node(AST_String, self, { value: "" }), 10515 right: elements[0], 10516 }); 10517 } 10518 // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3" 10519 if (separator == "") { 10520 var first; 10521 if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) { 10522 first = elements.shift(); 10523 } else { 10524 first = make_node(AST_String, self, { value: "" }); 10525 } 10526 return elements.reduce(function(prev, el) { 10527 return make_node(AST_Binary, el, { 10528 operator: "+", 10529 left: prev, 10530 right: el, 10531 }); 10532 }, first).optimize(compressor); 10533 } 10534 // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join() 10535 // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-") 10536 // need this awkward cloning to not affect original element 10537 // best_of will decide which one to get through. 10538 var node = self.clone(); 10539 node.expression = node.expression.clone(); 10540 node.expression.expression = node.expression.expression.clone(); 10541 node.expression.expression.elements = elements; 10542 return best_of(compressor, self, node); 10543 } 10544 break; 10545 case "charAt": 10546 if (self.args.length < 2) { 10547 var node = make_node(AST_Binary, self, { 10548 operator: "||", 10549 left: make_node(AST_Sub, self, { 10550 expression: exp.expression, 10551 property: self.args.length ? make_node(AST_Binary, self.args[0], { 10552 operator: "|", 10553 left: make_node(AST_Number, self, { value: 0 }), 10554 right: self.args[0], 10555 }) : make_node(AST_Number, self, { value: 0 }), 10556 }).optimize(compressor), 10557 right: make_node(AST_String, self, { value: "" }), 10558 }); 10559 node.is_string = return_true; 10560 return node.optimize(compressor); 10561 } 10562 break; 10563 case "apply": 10564 if (self.args.length == 2 && self.args[1] instanceof AST_Array) { 10565 var args = self.args[1].elements.slice(); 10566 args.unshift(self.args[0]); 10567 return make_node(AST_Call, self, { 10568 expression: make_node(AST_Dot, exp, { 10569 expression: exp.expression, 10570 property: "call", 10571 }), 10572 args: args, 10573 }).optimize(compressor); 10574 } 10575 break; 10576 case "call": 10577 var func = exp.expression; 10578 if (func instanceof AST_SymbolRef) { 10579 func = func.fixed_value(); 10580 } 10581 if (func instanceof AST_Lambda && !func.contains_this()) { 10582 return (self.args.length ? make_sequence(self, [ 10583 self.args[0], 10584 make_node(AST_Call, self, { 10585 expression: exp.expression, 10586 args: self.args.slice(1), 10587 }), 10588 ]) : make_node(AST_Call, self, { 10589 expression: exp.expression, 10590 args: [], 10591 })).optimize(compressor); 10592 } 10593 break; 10594 } else if (compressor.option("side_effects") 10595 && exp instanceof AST_Call 10596 && exp.args.length == 1 10597 && is_undeclared_ref(exp.expression) 10598 && exp.expression.name == "Object") { 10599 var call = self.clone(); 10600 call.expression = maintain_this_binding(self, exp, exp.args[0]); 10601 return call.optimize(compressor); 10602 } 10603 } 10604 if (compressor.option("unsafe_Function") 10605 && is_undeclared_ref(exp) 10606 && exp.name == "Function") { 10607 // new Function() ---> function(){} 10608 if (self.args.length == 0) return make_node(AST_Function, self, { 10609 argnames: [], 10610 body: [], 10611 }).init_vars(exp.scope); 10612 if (all(self.args, function(x) { 10613 return x instanceof AST_String; 10614 })) { 10615 // quite a corner-case, but we can handle it: 10616 // https://github.com/mishoo/UglifyJS/issues/203 10617 // if the code argument is a constant, then we can minify it. 10618 try { 10619 var code = "n(function(" + self.args.slice(0, -1).map(function(arg) { 10620 return arg.value; 10621 }).join() + "){" + self.args[self.args.length - 1].value + "})"; 10622 var ast = parse(code); 10623 var mangle = { ie: compressor.option("ie") }; 10624 ast.figure_out_scope(mangle); 10625 var comp = new Compressor(compressor.options); 10626 ast = ast.transform(comp); 10627 ast.figure_out_scope(mangle); 10628 ast.compute_char_frequency(mangle); 10629 ast.mangle_names(mangle); 10630 var fun; 10631 ast.walk(new TreeWalker(function(node) { 10632 if (fun) return true; 10633 if (node instanceof AST_Lambda) { 10634 fun = node; 10635 return true; 10636 } 10637 })); 10638 var code = OutputStream(); 10639 AST_BlockStatement.prototype._codegen.call(fun, code); 10640 self.args = [ 10641 make_node(AST_String, self, { 10642 value: fun.argnames.map(function(arg) { 10643 return arg.print_to_string(); 10644 }).join(), 10645 }), 10646 make_node(AST_String, self.args[self.args.length - 1], { 10647 value: code.get().replace(/^\{|\}$/g, "") 10648 }), 10649 ]; 10650 return self; 10651 } catch (ex) { 10652 if (ex instanceof JS_Parse_Error) { 10653 AST_Node.warn("Error parsing code passed to new Function [{start}]", self.args[self.args.length - 1]); 10654 AST_Node.warn(ex.toString()); 10655 } else { 10656 throw ex; 10657 } 10658 } 10659 } 10660 } 10661 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp; 10662 var parent = compressor.parent(), current = compressor.self(); 10663 var is_func = fn instanceof AST_Lambda 10664 && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await) 10665 && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested); 10666 var stat = is_func && fn.first_statement(); 10667 var has_default = 0, has_destructured = false; 10668 var has_spread = !all(self.args, function(arg) { 10669 return !(arg instanceof AST_Spread); 10670 }); 10671 var can_drop = is_func && all(fn.argnames, function(argname, index) { 10672 if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2; 10673 if (argname instanceof AST_DefaultValue) { 10674 if (!has_default) has_default = 1; 10675 var arg = has_default == 1 && self.args[index]; 10676 if (!is_undefined(arg)) has_default = 2; 10677 if (has_arg_refs(fn, argname.value)) return false; 10678 argname = argname.name; 10679 } 10680 if (argname instanceof AST_Destructured) { 10681 has_destructured = true; 10682 if (has_arg_refs(fn, argname)) return false; 10683 } 10684 return true; 10685 }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn, fn.rest)); 10686 var can_inline = can_drop 10687 && compressor.option("inline") 10688 && !self.is_expr_pure(compressor) 10689 && (exp === fn || safe_from_strict_mode(fn, compressor)); 10690 if (can_inline && stat instanceof AST_Return) { 10691 var value = stat.value; 10692 if (exp === fn 10693 && !fn.name 10694 && (!value || value.is_constant_expression()) 10695 && safe_from_await_yield(fn, avoid_await_yield(compressor))) { 10696 return make_sequence(self, convert_args(value)).optimize(compressor); 10697 } 10698 } 10699 if (is_func && !fn.contains_this()) { 10700 var def, value, var_assigned = false; 10701 if (can_inline 10702 && !fn.uses_arguments 10703 && !fn.pinned() 10704 && !(fn.name && fn instanceof AST_LambdaExpression) 10705 && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn) 10706 && fn.is_constant_expression(find_scope(compressor))) 10707 && (value = can_flatten_body(stat))) { 10708 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1; 10709 if (can_substitute_directly()) { 10710 var args = self.args.slice(); 10711 var refs = []; 10712 var retValue = value.clone(true).transform(new TreeTransformer(function(node) { 10713 if (node instanceof AST_SymbolRef) { 10714 var def = node.definition(); 10715 if (fn.variables.get(node.name) !== def) { 10716 refs.push(node); 10717 return node; 10718 } 10719 var index = resolve_index(def); 10720 var arg = args[index]; 10721 if (!arg) return make_node(AST_Undefined, self); 10722 args[index] = null; 10723 var parent = this.parent(); 10724 return parent ? maintain_this_binding(parent, node, arg) : arg; 10725 } 10726 })); 10727 var save_inlined = fn.inlined; 10728 if (exp !== fn) fn.inlined = true; 10729 var exprs = []; 10730 args.forEach(function(arg) { 10731 if (!arg) return; 10732 arg = arg.clone(true); 10733 arg.walk(new TreeWalker(function(node) { 10734 if (node instanceof AST_SymbolRef) refs.push(node); 10735 })); 10736 exprs.push(arg); 10737 }, []); 10738 exprs.push(retValue); 10739 var node = make_sequence(self, exprs).optimize(compressor); 10740 fn.inlined = save_inlined; 10741 node = maintain_this_binding(parent, current, node); 10742 if (replacing || best_of_expression(node, self) === node) { 10743 refs.forEach(function(ref) { 10744 ref.scope = exp === fn ? fn.parent_scope : exp.scope; 10745 ref.reference(); 10746 var def = ref.definition(); 10747 if (replacing) def.replaced++; 10748 def.single_use = false; 10749 }); 10750 return node; 10751 } else if (!node.has_side_effects(compressor)) { 10752 self.drop_side_effect_free = function(compressor, first_in_statement) { 10753 var self = this; 10754 var exprs = self.args.slice(); 10755 exprs.unshift(self.expression); 10756 return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement); 10757 }; 10758 } 10759 } 10760 var arg_used, insert, in_loop, scope; 10761 if (replacing && can_inject_symbols()) { 10762 fn._squeezed = true; 10763 if (exp !== fn) fn.parent_scope = exp.scope; 10764 var node = make_sequence(self, flatten_fn()).optimize(compressor); 10765 return maintain_this_binding(parent, current, node); 10766 } 10767 } 10768 if (compressor.option("side_effects") 10769 && can_drop 10770 && all(fn.body, is_empty) 10771 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest) 10772 && !(is_arrow(fn) && fn.value) 10773 && safe_from_await_yield(fn, avoid_await_yield(compressor))) { 10774 return make_sequence(self, convert_args()).optimize(compressor); 10775 } 10776 } 10777 if (compressor.option("drop_console")) { 10778 if (exp instanceof AST_PropAccess) { 10779 var name = exp.expression; 10780 while (name.expression) { 10781 name = name.expression; 10782 } 10783 if (is_undeclared_ref(name) && name.name == "console") { 10784 return make_node(AST_Undefined, self).optimize(compressor); 10785 } 10786 } 10787 } 10788 if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) { 10789 return self.negate(compressor, true); 10790 } 10791 return try_evaluate(compressor, self); 10792 10793 function make_void_lhs(orig) { 10794 return make_node(AST_Sub, orig, { 10795 expression: make_node(AST_Array, orig, { elements: [] }), 10796 property: make_node(AST_Number, orig, { value: 0 }), 10797 }); 10798 } 10799 10800 function convert_args(value) { 10801 var args = self.args.slice(); 10802 var destructured = has_default > 1 || has_destructured || fn.rest; 10803 if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ]; 10804 if (destructured) { 10805 var tt = new TreeTransformer(function(node, descend) { 10806 if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, { 10807 name: node.name.transform(tt) || make_void_lhs(node), 10808 value: node.value, 10809 }); 10810 if (node instanceof AST_DestructuredArray) { 10811 var elements = []; 10812 node.elements.forEach(function(node, index) { 10813 node = node.transform(tt); 10814 if (node) elements[index] = node; 10815 }); 10816 fill_holes(node, elements); 10817 return make_node(AST_DestructuredArray, node, { elements: elements }); 10818 } 10819 if (node instanceof AST_DestructuredObject) { 10820 var properties = [], side_effects = []; 10821 node.properties.forEach(function(prop) { 10822 var key = prop.key; 10823 var value = prop.value.transform(tt); 10824 if (value) { 10825 if (side_effects.length) { 10826 if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop); 10827 side_effects.push(key); 10828 key = make_sequence(node, side_effects); 10829 side_effects = []; 10830 } 10831 properties.push(make_node(AST_DestructuredKeyVal, prop, { 10832 key: key, 10833 value: value, 10834 })); 10835 } else if (key instanceof AST_Node) { 10836 side_effects.push(key); 10837 } 10838 }); 10839 if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, { 10840 key: make_sequence(node, side_effects), 10841 value: make_void_lhs(node), 10842 })); 10843 return make_node(AST_DestructuredObject, node, { properties: properties }); 10844 } 10845 if (node instanceof AST_SymbolFunarg) return null; 10846 }); 10847 var lhs = []; 10848 fn.argnames.forEach(function(argname, index) { 10849 argname = argname.transform(tt); 10850 if (argname) lhs[index] = argname; 10851 }); 10852 var rest = fn.rest && fn.rest.transform(tt); 10853 if (rest) lhs.length = fn.argnames.length; 10854 fill_holes(fn, lhs); 10855 args[0] = make_node(AST_Assign, self, { 10856 operator: "=", 10857 left: make_node(AST_DestructuredArray, fn, { 10858 elements: lhs, 10859 rest: rest, 10860 }), 10861 right: args[0], 10862 }); 10863 } else fn.argnames.forEach(function(argname) { 10864 if (argname instanceof AST_DefaultValue) args.push(argname.value); 10865 }); 10866 args.push(value || make_node(AST_Undefined, self)); 10867 return args; 10868 } 10869 10870 function noop_value() { 10871 return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self); 10872 } 10873 10874 function return_value(stat) { 10875 if (!stat) return noop_value(); 10876 if (stat instanceof AST_Return) return stat.value || noop_value(); 10877 if (stat instanceof AST_SimpleStatement) { 10878 return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, { 10879 operator: "void", 10880 expression: stat.body, 10881 }); 10882 } 10883 } 10884 10885 function can_flatten_body(stat) { 10886 var len = fn.body.length; 10887 if (len < 2) { 10888 stat = return_value(stat); 10889 if (stat) return stat; 10890 } 10891 if (compressor.option("inline") < 3) return false; 10892 stat = null; 10893 for (var i = 0; i < len; i++) { 10894 var line = fn.body[i]; 10895 if (line instanceof AST_Var) { 10896 if (var_assigned) { 10897 if (!stat) continue; 10898 if (!(stat instanceof AST_SimpleStatement)) return false; 10899 if (!declarations_only(line)) stat = null; 10900 } else if (!declarations_only(line)) { 10901 if (stat && !(stat instanceof AST_SimpleStatement)) return false; 10902 stat = null; 10903 var_assigned = true; 10904 } 10905 } else if (line instanceof AST_AsyncDefun 10906 || line instanceof AST_Defun 10907 || line instanceof AST_EmptyStatement) { 10908 continue; 10909 } else if (stat) { 10910 return false; 10911 } else { 10912 stat = line; 10913 } 10914 } 10915 return return_value(stat); 10916 } 10917 10918 function resolve_index(def) { 10919 for (var i = fn.argnames.length; --i >= 0;) { 10920 if (fn.argnames[i].definition() === def) return i; 10921 } 10922 } 10923 10924 function can_substitute_directly() { 10925 if (has_default || has_destructured || has_spread || var_assigned || fn.rest) return; 10926 if (compressor.option("inline") < 2 && fn.argnames.length) return; 10927 if (!fn.variables.all(function(def) { 10928 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg; 10929 })) return; 10930 var scope = compressor.find_parent(AST_Scope); 10931 var abort = false; 10932 var avoid = avoid_await_yield(compressor, scope); 10933 var begin; 10934 var in_order = []; 10935 var side_effects = false; 10936 var tw = new TreeWalker(function(node, descend) { 10937 if (abort) return true; 10938 if (node instanceof AST_Binary && lazy_op[node.operator] 10939 || node instanceof AST_Conditional) { 10940 in_order = null; 10941 return; 10942 } 10943 if (node instanceof AST_Scope) return abort = true; 10944 if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true; 10945 if (node instanceof AST_SymbolRef) { 10946 var def = node.definition(); 10947 if (fn.variables.get(node.name) !== def) { 10948 in_order = null; 10949 return; 10950 } 10951 if (def.init instanceof AST_LambdaDefinition) return abort = true; 10952 if (is_lhs(node, tw.parent())) return abort = true; 10953 var index = resolve_index(def); 10954 if (!(begin < index)) begin = index; 10955 if (!in_order) return; 10956 if (side_effects) { 10957 in_order = null; 10958 } else { 10959 in_order.push(fn.argnames[index]); 10960 } 10961 return; 10962 } 10963 if (side_effects) return; 10964 if (node instanceof AST_Assign && node.left instanceof AST_PropAccess) { 10965 node.left.expression.walk(tw); 10966 if (node.left instanceof AST_Sub) node.left.property.walk(tw); 10967 node.right.walk(tw); 10968 side_effects = true; 10969 return true; 10970 } 10971 if (node.has_side_effects(compressor)) { 10972 descend(); 10973 side_effects = true; 10974 return true; 10975 } 10976 }); 10977 value.walk(tw); 10978 if (abort) return; 10979 var end = self.args.length; 10980 if (in_order && fn.argnames.length >= end) { 10981 end = fn.argnames.length; 10982 while (end-- > begin && fn.argnames[end] === in_order.pop()); 10983 end++; 10984 } 10985 return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) { 10986 return funarg.is_constant_expression(scope); 10987 } : function(funarg) { 10988 return !funarg.has_side_effects(compressor); 10989 }); 10990 } 10991 10992 function var_exists(defined, name) { 10993 return defined.has(name) || identifier_atom[name] || scope.var_names().has(name); 10994 } 10995 10996 function can_inject_args(defined, safe_to_inject) { 10997 var abort = false; 10998 fn.each_argname(function(arg) { 10999 if (abort) return; 11000 if (arg.unused) return; 11001 if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true; 11002 arg_used.set(arg.name, true); 11003 if (in_loop) in_loop.push(arg.definition()); 11004 }); 11005 return !abort; 11006 } 11007 11008 function can_inject_vars(defined, safe_to_inject) { 11009 for (var i = 0; i < fn.body.length; i++) { 11010 var stat = fn.body[i]; 11011 if (stat instanceof AST_LambdaDefinition) { 11012 var name = stat.name; 11013 if (!safe_to_inject) return false; 11014 if (arg_used.has(name.name)) return false; 11015 if (var_exists(defined, name.name)) return false; 11016 if (!all(stat.enclosed, function(def) { 11017 return def.scope === scope || def.scope === stat || !defined.has(def.name); 11018 })) return false; 11019 if (in_loop) in_loop.push(name.definition()); 11020 continue; 11021 } 11022 if (!(stat instanceof AST_Var)) continue; 11023 if (!safe_to_inject) return false; 11024 for (var j = stat.definitions.length; --j >= 0;) { 11025 var name = stat.definitions[j].name; 11026 if (var_exists(defined, name.name)) return false; 11027 if (in_loop) in_loop.push(name.definition()); 11028 } 11029 } 11030 return true; 11031 } 11032 11033 function can_inject_symbols() { 11034 var defined = new Dictionary(); 11035 var level = 0, child; 11036 scope = current; 11037 do { 11038 if (scope.variables) scope.variables.each(function(def) { 11039 defined.set(def.name, true); 11040 }); 11041 child = scope; 11042 scope = compressor.parent(level++); 11043 if (scope instanceof AST_ClassField) { 11044 if (!scope.static) return false; 11045 } else if (scope instanceof AST_DWLoop) { 11046 in_loop = []; 11047 } else if (scope instanceof AST_For) { 11048 if (scope.init === child) continue; 11049 in_loop = []; 11050 } else if (scope instanceof AST_ForEnumeration) { 11051 if (scope.init === child) continue; 11052 if (scope.object === child) continue; 11053 in_loop = []; 11054 } 11055 } while (!(scope instanceof AST_Scope)); 11056 insert = scope.body.indexOf(child) + 1; 11057 if (!insert) return false; 11058 if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return false; 11059 var safe_to_inject = (exp !== fn || fn.parent_scope.resolve() === scope) && !scope.pinned(); 11060 if (scope instanceof AST_Toplevel) { 11061 if (compressor.toplevel.vars) { 11062 defined.set("arguments", true); 11063 } else { 11064 safe_to_inject = false; 11065 } 11066 } 11067 arg_used = new Dictionary(); 11068 var inline = compressor.option("inline"); 11069 if (!can_inject_args(defined, inline >= 2 && safe_to_inject)) return false; 11070 if (!can_inject_vars(defined, inline >= 3 && safe_to_inject)) return false; 11071 return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop); 11072 } 11073 11074 function append_var(decls, expressions, name, value) { 11075 var def = name.definition(); 11076 if (!scope.var_names().has(name.name)) { 11077 scope.var_names().set(name.name, true); 11078 decls.push(make_node(AST_VarDef, name, { 11079 name: name, 11080 value: null, 11081 })); 11082 } 11083 scope.variables.set(name.name, def); 11084 scope.enclosed.push(def); 11085 if (!value) return; 11086 var sym = make_node(AST_SymbolRef, name); 11087 def.assignments++; 11088 def.references.push(sym); 11089 expressions.push(make_node(AST_Assign, self, { 11090 operator: "=", 11091 left: sym, 11092 right: value, 11093 })); 11094 } 11095 11096 function flatten_args(decls, expressions) { 11097 var len = fn.argnames.length; 11098 for (var i = self.args.length; --i >= len;) { 11099 expressions.push(self.args[i]); 11100 } 11101 var default_args = []; 11102 for (i = len; --i >= 0;) { 11103 var argname = fn.argnames[i]; 11104 var name; 11105 if (argname instanceof AST_DefaultValue) { 11106 default_args.push(argname); 11107 name = argname.name; 11108 } else { 11109 name = argname; 11110 } 11111 var value = self.args[i]; 11112 if (name.unused || scope.var_names().has(name.name)) { 11113 if (value) expressions.push(value); 11114 } else { 11115 var symbol = make_node(AST_SymbolVar, name); 11116 var def = name.definition(); 11117 def.orig.push(symbol); 11118 def.eliminated++; 11119 if (name.unused !== undefined) { 11120 append_var(decls, expressions, symbol); 11121 if (value) expressions.push(value); 11122 } else { 11123 if (!value && argname === name && (in_loop 11124 || name.name == "arguments" && !is_arrow(fn) && is_arrow(scope))) { 11125 value = make_node(AST_Undefined, self); 11126 } 11127 append_var(decls, expressions, symbol, value); 11128 } 11129 } 11130 } 11131 decls.reverse(); 11132 expressions.reverse(); 11133 for (i = default_args.length; --i >= 0;) { 11134 var node = default_args[i]; 11135 if (node.name.unused !== undefined) { 11136 expressions.push(node.value); 11137 } else { 11138 var sym = make_node(AST_SymbolRef, node.name); 11139 node.name.definition().references.push(sym); 11140 expressions.push(make_node(AST_Assign, node, { 11141 operator: "=", 11142 left: sym, 11143 right: node.value, 11144 })); 11145 } 11146 } 11147 } 11148 11149 function flatten_destructured(decls, expressions) { 11150 expressions.push(make_node(AST_Assign, self, { 11151 operator: "=", 11152 left: make_node(AST_DestructuredArray, self, { 11153 elements: fn.argnames.map(function(argname) { 11154 if (argname.unused) return make_node(AST_Hole, argname); 11155 return argname.convert_symbol(AST_SymbolRef, process); 11156 }), 11157 rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process), 11158 }), 11159 right: make_node(AST_Array, self, { elements: self.args.slice() }), 11160 })); 11161 11162 function process(ref, name) { 11163 if (name.unused) return make_void_lhs(name); 11164 var def = name.definition(); 11165 def.assignments++; 11166 def.references.push(ref); 11167 var symbol = make_node(AST_SymbolVar, name); 11168 def.orig.push(symbol); 11169 def.eliminated++; 11170 append_var(decls, expressions, symbol); 11171 } 11172 } 11173 11174 function flatten_vars(decls, expressions) { 11175 var args = [ insert, 0 ]; 11176 var decl_var = [], expr_fn = [], expr_var = [], expr_loop = [], exprs = []; 11177 fn.body.filter(in_loop ? function(stat) { 11178 if (!(stat instanceof AST_LambdaDefinition)) return true; 11179 var name = make_node(AST_SymbolVar, flatten_var(stat.name)); 11180 var def = name.definition(); 11181 def.fixed = false; 11182 def.orig.push(name); 11183 def.eliminated++; 11184 append_var(decls, expr_fn, name, to_func_expr(stat, true)); 11185 return false; 11186 } : function(stat) { 11187 if (!(stat instanceof AST_LambdaDefinition)) return true; 11188 var def = stat.name.definition(); 11189 scope.functions.set(def.name, def); 11190 scope.variables.set(def.name, def); 11191 scope.enclosed.push(def); 11192 scope.var_names().set(def.name, true); 11193 args.push(stat); 11194 return false; 11195 }).forEach(function(stat) { 11196 if (!(stat instanceof AST_Var)) { 11197 if (stat instanceof AST_SimpleStatement) exprs.push(stat.body); 11198 return; 11199 } 11200 for (var j = 0; j < stat.definitions.length; j++) { 11201 var var_def = stat.definitions[j]; 11202 var name = flatten_var(var_def.name); 11203 var value = var_def.value; 11204 if (value && exprs.length > 0) { 11205 exprs.push(value); 11206 value = make_sequence(var_def, exprs); 11207 exprs = []; 11208 } 11209 append_var(decl_var, expr_var, name, value); 11210 if (!in_loop) continue; 11211 if (arg_used.has(name.name)) continue; 11212 if (name.definition().orig.length == 1 && fn.functions.has(name.name)) continue; 11213 expr_loop.push(init_ref(compressor, name)); 11214 } 11215 }); 11216 [].push.apply(decls, decl_var); 11217 [].push.apply(expressions, expr_loop); 11218 [].push.apply(expressions, expr_fn); 11219 [].push.apply(expressions, expr_var); 11220 return args; 11221 } 11222 11223 function flatten_fn() { 11224 var decls = []; 11225 var expressions = []; 11226 if (has_default > 1 || has_destructured || has_spread || fn.rest) { 11227 flatten_destructured(decls, expressions); 11228 } else { 11229 flatten_args(decls, expressions); 11230 } 11231 var args = flatten_vars(decls, expressions); 11232 expressions.push(value); 11233 if (decls.length) args.push(make_node(AST_Var, fn, { definitions: decls })); 11234 [].splice.apply(scope.body, args); 11235 fn.enclosed.forEach(function(def) { 11236 if (scope.var_names().has(def.name)) return; 11237 scope.enclosed.push(def); 11238 scope.var_names().set(def.name, true); 11239 }); 11240 return expressions; 11241 } 11242 }); 11243 11244 OPT(AST_New, function(self, compressor) { 11245 if (compressor.option("unsafe")) { 11246 var exp = self.expression; 11247 if (is_undeclared_ref(exp)) switch (exp.name) { 11248 case "Array": 11249 case "Error": 11250 case "Function": 11251 case "Object": 11252 case "RegExp": 11253 return make_node(AST_Call, self).transform(compressor); 11254 } 11255 } 11256 if (compressor.option("sequences")) { 11257 var seq = lift_sequence_in_expression(self, compressor); 11258 if (seq !== self) return seq.optimize(compressor); 11259 } 11260 if (compressor.option("unused")) drop_unused_call_args(self, compressor); 11261 return self; 11262 }); 11263 11264 // (a = b, x && a = c) ---> a = x ? c : b 11265 // (a = b, x || a = c) ---> a = x ? b : c 11266 function to_conditional_assignment(compressor, def, value, node) { 11267 if (!(node instanceof AST_Binary)) return; 11268 if (!(node.operator == "&&" || node.operator == "||")) return; 11269 if (!(node.right instanceof AST_Assign)) return; 11270 if (node.right.operator != "=") return; 11271 if (!(node.right.left instanceof AST_SymbolRef)) return; 11272 if (node.right.left.definition() !== def) return; 11273 if (value.has_side_effects(compressor)) return; 11274 if (!safe_from_assignment(node.left)) return; 11275 if (!safe_from_assignment(node.right.right)) return; 11276 def.replaced++; 11277 return node.operator == "&&" ? make_node(AST_Conditional, node, { 11278 condition: node.left, 11279 consequent: node.right.right, 11280 alternative: value, 11281 }) : make_node(AST_Conditional, node, { 11282 condition: node.left, 11283 consequent: value, 11284 alternative: node.right.right, 11285 }); 11286 11287 function safe_from_assignment(node) { 11288 if (node.has_side_effects(compressor)) return; 11289 var hit = false; 11290 node.walk(new TreeWalker(function(node) { 11291 if (hit) return true; 11292 if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true; 11293 })); 11294 return !hit; 11295 } 11296 } 11297 11298 OPT(AST_Sequence, function(self, compressor) { 11299 var expressions = filter_for_side_effects(); 11300 var end = expressions.length - 1; 11301 merge_assignments(); 11302 trim_right_for_undefined(); 11303 if (end == 0) { 11304 self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]); 11305 if (!(self instanceof AST_Sequence)) self = self.optimize(compressor); 11306 return self; 11307 } 11308 self.expressions = expressions; 11309 return self; 11310 11311 function filter_for_side_effects() { 11312 if (!compressor.option("side_effects")) return self.expressions; 11313 var expressions = []; 11314 var first = first_in_statement(compressor); 11315 var last = self.expressions.length - 1; 11316 self.expressions.forEach(function(expr, index) { 11317 if (index < last) expr = expr.drop_side_effect_free(compressor, first); 11318 if (expr) { 11319 merge_sequence(expressions, expr); 11320 first = false; 11321 } 11322 }); 11323 return expressions; 11324 } 11325 11326 function trim_right_for_undefined() { 11327 if (!compressor.option("side_effects")) return; 11328 while (end > 0 && is_undefined(expressions[end], compressor)) end--; 11329 if (end < expressions.length - 1) { 11330 expressions[end] = make_node(AST_UnaryPrefix, self, { 11331 operator: "void", 11332 expression: expressions[end], 11333 }); 11334 expressions.length = end + 1; 11335 } 11336 } 11337 11338 function is_simple_assign(node) { 11339 return node instanceof AST_Assign 11340 && node.operator == "=" 11341 && node.left instanceof AST_SymbolRef 11342 && node.left.definition(); 11343 } 11344 11345 function merge_assignments() { 11346 for (var i = 1; i < end; i++) { 11347 var prev = expressions[i - 1]; 11348 var def = is_simple_assign(prev); 11349 if (!def) continue; 11350 var expr = expressions[i]; 11351 if (compressor.option("conditionals")) { 11352 var cond = to_conditional_assignment(compressor, def, prev.right, expr); 11353 if (cond) { 11354 prev.right = cond; 11355 expressions.splice(i--, 1); 11356 end--; 11357 continue; 11358 } 11359 } 11360 if (compressor.option("dead_code") 11361 && is_simple_assign(expr) === def 11362 && expr.right.is_constant_expression(def.scope.resolve())) { 11363 expressions[--i] = prev.right; 11364 } 11365 } 11366 } 11367 }); 11368 11369 OPT(AST_UnaryPostfix, function(self, compressor) { 11370 if (compressor.option("sequences")) { 11371 var seq = lift_sequence_in_expression(self, compressor); 11372 if (seq !== self) return seq.optimize(compressor); 11373 } 11374 return try_evaluate(compressor, self); 11375 }); 11376 11377 var SIGN_OPS = makePredicate("+ -"); 11378 var MULTIPLICATIVE_OPS = makePredicate("* / %"); 11379 OPT(AST_UnaryPrefix, function(self, compressor) { 11380 var op = self.operator; 11381 var exp = self.expression; 11382 if (compressor.option("sequences") && can_lift()) { 11383 var seq = lift_sequence_in_expression(self, compressor); 11384 if (seq !== self) return seq.optimize(compressor); 11385 } 11386 switch (op) { 11387 case "+": 11388 if (!compressor.option("evaluate")) break; 11389 if (!exp.is_number(compressor, true)) break; 11390 var parent = compressor.parent(); 11391 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") break; 11392 return exp; 11393 case "-": 11394 if (exp instanceof AST_Infinity) exp = exp.transform(compressor); 11395 // avoids infinite recursion of numerals 11396 if (exp instanceof AST_Number || exp instanceof AST_Infinity) return self; 11397 break; 11398 case "!": 11399 if (!compressor.option("booleans")) break; 11400 if (exp.is_truthy()) return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor); 11401 if (compressor.in_boolean_context()) { 11402 // !!foo ---> foo, if we're in boolean context 11403 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") return exp.expression; 11404 if (exp instanceof AST_Binary) { 11405 var first = first_in_statement(compressor); 11406 self = (first ? best_of_statement : best_of_expression)(self, exp.negate(compressor, first)); 11407 } 11408 } 11409 break; 11410 case "delete": 11411 if (!compressor.option("evaluate")) break; 11412 if (may_not_delete(exp)) break; 11413 return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor); 11414 case "typeof": 11415 if (!compressor.option("booleans")) break; 11416 if (!compressor.in_boolean_context()) break; 11417 // typeof always returns a non-empty string, thus always truthy 11418 AST_Node.warn("Boolean expression always true [{start}]", self); 11419 var exprs = [ make_node(AST_True, self) ]; 11420 if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp); 11421 return make_sequence(self, exprs).optimize(compressor); 11422 case "void": 11423 if (!compressor.option("side_effects")) break; 11424 exp = exp.drop_side_effect_free(compressor); 11425 if (!exp) return make_node(AST_Undefined, self).optimize(compressor); 11426 self.expression = exp; 11427 return self; 11428 } 11429 if (compressor.option("evaluate") 11430 && exp instanceof AST_Binary 11431 && SIGN_OPS[op] 11432 && MULTIPLICATIVE_OPS[exp.operator] 11433 && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) { 11434 return make_node(AST_Binary, self, { 11435 operator: exp.operator, 11436 left: make_node(AST_UnaryPrefix, exp.left, { 11437 operator: op, 11438 expression: exp.left, 11439 }), 11440 right: exp.right, 11441 }); 11442 } 11443 return try_evaluate(compressor, self); 11444 11445 function may_not_delete(node) { 11446 return node instanceof AST_Infinity 11447 || node instanceof AST_NaN 11448 || node instanceof AST_NewTarget 11449 || node instanceof AST_PropAccess 11450 || node instanceof AST_SymbolRef 11451 || node instanceof AST_Undefined; 11452 } 11453 11454 function can_lift() { 11455 switch (op) { 11456 case "delete": 11457 return !may_not_delete(exp.tail_node()); 11458 case "typeof": 11459 return !is_undeclared_ref(exp.tail_node()); 11460 default: 11461 return true; 11462 } 11463 } 11464 }); 11465 11466 OPT(AST_Await, function(self, compressor) { 11467 if (!compressor.option("awaits")) return self; 11468 if (compressor.option("sequences")) { 11469 var seq = lift_sequence_in_expression(self, compressor); 11470 if (seq !== self) return seq.optimize(compressor); 11471 } 11472 if (compressor.option("side_effects")) { 11473 var exp = self.expression; 11474 if (exp instanceof AST_Await) return exp.optimize(compressor); 11475 if (exp instanceof AST_UnaryPrefix && exp.expression instanceof AST_Await) return exp.optimize(compressor); 11476 for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) { 11477 if (is_arrow(parent)) { 11478 if (parent.value === node) return exp.optimize(compressor); 11479 } else if (parent instanceof AST_Return) { 11480 var drop = true; 11481 do { 11482 node = parent; 11483 parent = compressor.parent(level++); 11484 if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) { 11485 drop = false; 11486 break; 11487 } 11488 } while (parent && !(parent instanceof AST_Scope)); 11489 if (drop) return exp.optimize(compressor); 11490 } else if (parent instanceof AST_Sequence) { 11491 if (parent.tail_node() === node) continue; 11492 } 11493 break; 11494 } 11495 } 11496 return self; 11497 }); 11498 11499 OPT(AST_Yield, function(self, compressor) { 11500 if (!compressor.option("yields")) return self; 11501 if (compressor.option("sequences")) { 11502 var seq = lift_sequence_in_expression(self, compressor); 11503 if (seq !== self) return seq.optimize(compressor); 11504 } 11505 var exp = self.expression; 11506 if (self.nested && exp.TYPE == "Call") { 11507 var inlined = exp.clone().optimize(compressor); 11508 if (inlined.TYPE != "Call") return inlined; 11509 } 11510 return self; 11511 }); 11512 11513 AST_Binary.DEFMETHOD("lift_sequences", function(compressor) { 11514 if (this.left instanceof AST_PropAccess) { 11515 if (!(this.left.expression instanceof AST_Sequence)) return this; 11516 var x = this.left.expression.expressions.slice(); 11517 var e = this.clone(); 11518 e.left = e.left.clone(); 11519 e.left.expression = x.pop(); 11520 x.push(e); 11521 return make_sequence(this, x); 11522 } 11523 if (this.left instanceof AST_Sequence) { 11524 var x = this.left.expressions.slice(); 11525 var e = this.clone(); 11526 e.left = x.pop(); 11527 x.push(e); 11528 return make_sequence(this, x); 11529 } 11530 if (this.right instanceof AST_Sequence) { 11531 if (this.left.has_side_effects(compressor)) return this; 11532 var assign = this.operator == "=" && this.left instanceof AST_SymbolRef; 11533 var x = this.right.expressions; 11534 var last = x.length - 1; 11535 for (var i = 0; i < last; i++) { 11536 if (!assign && x[i].has_side_effects(compressor)) break; 11537 } 11538 if (i == last) { 11539 x = x.slice(); 11540 var e = this.clone(); 11541 e.right = x.pop(); 11542 x.push(e); 11543 return make_sequence(this, x); 11544 } 11545 if (i > 0) { 11546 var e = this.clone(); 11547 e.right = make_sequence(this.right, x.slice(i)); 11548 x = x.slice(0, i); 11549 x.push(e); 11550 return make_sequence(this, x); 11551 } 11552 } 11553 return this; 11554 }); 11555 11556 var indexFns = makePredicate("indexOf lastIndexOf"); 11557 var commutativeOperators = makePredicate("== === != !== * & | ^"); 11558 function is_object(node, plain) { 11559 if (node instanceof AST_Assign) return !plain && node.operator == "=" && is_object(node.right); 11560 if (node instanceof AST_New) return !plain; 11561 if (node instanceof AST_Sequence) return is_object(node.tail_node(), plain); 11562 if (node instanceof AST_SymbolRef) return !plain && is_object(node.fixed_value()); 11563 return node instanceof AST_Array 11564 || node instanceof AST_Class 11565 || node instanceof AST_Lambda 11566 || node instanceof AST_Object; 11567 } 11568 11569 function can_drop_op(op, rhs, compressor) { 11570 switch (op) { 11571 case "in": 11572 return is_object(rhs) || compressor && compressor.option("unsafe_comps"); 11573 case "instanceof": 11574 if (rhs instanceof AST_SymbolRef) rhs = rhs.fixed_value(); 11575 return is_lambda(rhs) || compressor && compressor.option("unsafe_comps"); 11576 default: 11577 return true; 11578 } 11579 } 11580 11581 function needs_enqueuing(compressor, node) { 11582 if (node.is_constant()) return true; 11583 if (node instanceof AST_Assign) return node.operator != "=" || needs_enqueuing(compressor, node.right); 11584 if (node instanceof AST_Binary) { 11585 return !lazy_op[node.operator] 11586 || needs_enqueuing(compressor, node.left) && needs_enqueuing(compressor, node.right); 11587 } 11588 if (node instanceof AST_Call) return is_async(node.expression); 11589 if (node instanceof AST_Conditional) { 11590 return needs_enqueuing(compressor, node.consequent) && needs_enqueuing(compressor, node.alternative); 11591 } 11592 if (node instanceof AST_Sequence) return needs_enqueuing(compressor, node.tail_node()); 11593 if (node instanceof AST_SymbolRef) { 11594 var fixed = node.fixed_value(); 11595 return fixed && needs_enqueuing(compressor, fixed); 11596 } 11597 if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag); 11598 if (node instanceof AST_Unary) return true; 11599 } 11600 11601 function extract_lhs(node, compressor) { 11602 if (node instanceof AST_Assign) return is_lhs_read_only(node.left, compressor) ? node : node.left; 11603 if (node instanceof AST_Sequence) return extract_lhs(node.tail_node(), compressor); 11604 if (node instanceof AST_UnaryPrefix && UNARY_POSTFIX[node.operator]) { 11605 return is_lhs_read_only(node.expression, compressor) ? node : node.expression; 11606 } 11607 return node; 11608 } 11609 11610 function repeatable(compressor, node) { 11611 if (node instanceof AST_Dot) return repeatable(compressor, node.expression); 11612 if (node instanceof AST_Sub) { 11613 return repeatable(compressor, node.expression) && repeatable(compressor, node.property); 11614 } 11615 if (node instanceof AST_Symbol) return true; 11616 return !node.has_side_effects(compressor); 11617 } 11618 11619 function swap_chain(self, compressor) { 11620 var rhs = self.right.tail_node(); 11621 if (rhs !== self.right) { 11622 var exprs = self.right.expressions.slice(0, -1); 11623 exprs.push(rhs.left); 11624 rhs = rhs.clone(); 11625 rhs.left = make_sequence(self.right, exprs); 11626 self.right = rhs; 11627 } 11628 self.left = make_node(AST_Binary, self, { 11629 operator: self.operator, 11630 left: self.left, 11631 right: rhs.left, 11632 start: self.left.start, 11633 end: rhs.left.end, 11634 }); 11635 self.right = rhs.right; 11636 if (compressor) { 11637 self.left = self.left.transform(compressor); 11638 } else if (self.operator == rhs.left.operator) { 11639 swap_chain(self.left); 11640 } 11641 } 11642 11643 OPT(AST_Binary, function(self, compressor) { 11644 if (commutativeOperators[self.operator] 11645 && self.right.is_constant() 11646 && !self.left.is_constant() 11647 && !(self.left instanceof AST_Binary 11648 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { 11649 // if right is a constant, whatever side effects the 11650 // left side might have could not influence the 11651 // result. hence, force switch. 11652 reverse(); 11653 } 11654 if (compressor.option("sequences")) { 11655 var seq = self.lift_sequences(compressor); 11656 if (seq !== self) return seq.optimize(compressor); 11657 } 11658 if (compressor.option("assignments") && lazy_op[self.operator]) { 11659 var lhs = extract_lhs(self.left, compressor); 11660 var right = self.right; 11661 // a || (a = x) ---> a = a || x 11662 // (a = x) && (a = y) ---> a = (a = x) && y 11663 if (lhs instanceof AST_SymbolRef 11664 && right instanceof AST_Assign 11665 && right.operator == "=" 11666 && lhs.equals(right.left)) { 11667 lhs = lhs.clone(); 11668 var assign = make_node(AST_Assign, self, { 11669 operator: "=", 11670 left: lhs, 11671 right: make_node(AST_Binary, self, { 11672 operator: self.operator, 11673 left: self.left, 11674 right: right.right, 11675 }), 11676 }); 11677 if (lhs.fixed) { 11678 lhs.fixed = function() { 11679 return assign.right; 11680 }; 11681 lhs.fixed.assigns = [ assign ]; 11682 } 11683 var def = lhs.definition(); 11684 def.references.push(lhs); 11685 def.replaced++; 11686 return assign.optimize(compressor); 11687 } 11688 } 11689 if (compressor.option("comparisons")) switch (self.operator) { 11690 case "===": 11691 case "!==": 11692 if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) { 11693 AST_Node.warn("Expression always defined [{start}]", self); 11694 return make_sequence(self, [ 11695 self.right, 11696 make_node(self.operator == "===" ? AST_False : AST_True, self), 11697 ]).optimize(compressor); 11698 } 11699 var is_strict_comparison = true; 11700 if ((self.left.is_string(compressor) && self.right.is_string(compressor)) || 11701 (self.left.is_number(compressor) && self.right.is_number(compressor)) || 11702 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) || 11703 repeatable(compressor, self.left) && self.left.equals(self.right)) { 11704 self.operator = self.operator.slice(0, 2); 11705 } 11706 // XXX: intentionally falling down to the next case 11707 case "==": 11708 case "!=": 11709 // void 0 == x ---> null == x 11710 if (!is_strict_comparison && is_undefined(self.left, compressor)) { 11711 self.left = make_node(AST_Null, self.left); 11712 } 11713 // "undefined" == typeof x ---> undefined === x 11714 else if (compressor.option("typeofs") 11715 && self.left instanceof AST_String 11716 && self.left.value == "undefined" 11717 && self.right instanceof AST_UnaryPrefix 11718 && self.right.operator == "typeof") { 11719 var expr = self.right.expression; 11720 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor) 11721 : !(expr instanceof AST_PropAccess && compressor.option("ie"))) { 11722 self.right = expr; 11723 self.left = make_node(AST_Undefined, self.left).optimize(compressor); 11724 if (self.operator.length == 2) self.operator += "="; 11725 } 11726 } 11727 // obj !== obj ---> false 11728 else if (self.left instanceof AST_SymbolRef 11729 && self.right instanceof AST_SymbolRef 11730 && self.left.definition() === self.right.definition() 11731 && is_object(self.left)) { 11732 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor); 11733 } 11734 break; 11735 case "&&": 11736 case "||": 11737 // void 0 !== x && null !== x ---> null != x 11738 // void 0 === x || null === x ---> null == x 11739 var left = self.left; 11740 if (!(left instanceof AST_Binary)) break; 11741 if (left.operator != (self.operator == "&&" ? "!==" : "===")) break; 11742 if (!(self.right instanceof AST_Binary)) break; 11743 if (left.operator != self.right.operator) break; 11744 if (is_undefined(left.left, compressor) && self.right.left instanceof AST_Null 11745 || left.left instanceof AST_Null && is_undefined(self.right.left, compressor)) { 11746 var expr = left.right; 11747 if (expr instanceof AST_Assign && expr.operator == "=") expr = expr.left; 11748 if (expr.has_side_effects(compressor)) break; 11749 if (!expr.equals(self.right.right)) break; 11750 left.operator = left.operator.slice(0, -1); 11751 left.left = make_node(AST_Null, self); 11752 return left; 11753 } 11754 break; 11755 } 11756 var in_bool = false; 11757 var parent = compressor.parent(); 11758 if (compressor.option("booleans")) { 11759 var lhs = extract_lhs(self.left, compressor); 11760 if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) { 11761 // a || a ---> a 11762 // (a = x) && a --> a = x 11763 if (lhs.equals(self.right)) { 11764 return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor); 11765 } 11766 mark_duplicate_condition(compressor, lhs); 11767 } 11768 in_bool = compressor.in_boolean_context(); 11769 } 11770 if (in_bool) switch (self.operator) { 11771 case "+": 11772 var ev = self.left.evaluate(compressor, true); 11773 if (ev && typeof ev == "string" || (ev = self.right.evaluate(compressor, true)) && typeof ev == "string") { 11774 AST_Node.warn("+ in boolean context always true [{start}]", self); 11775 var exprs = []; 11776 if (self.left.evaluate(compressor) instanceof AST_Node) exprs.push(self.left); 11777 if (self.right.evaluate(compressor) instanceof AST_Node) exprs.push(self.right); 11778 if (exprs.length < 2) { 11779 exprs.push(make_node(AST_True, self)); 11780 return make_sequence(self, exprs).optimize(compressor); 11781 } 11782 self.truthy = true; 11783 } 11784 break; 11785 case "==": 11786 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) { 11787 return make_node(AST_UnaryPrefix, self, { 11788 operator: "!", 11789 expression: self.right, 11790 }).optimize(compressor); 11791 } 11792 break; 11793 case "!=": 11794 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) { 11795 return self.right.optimize(compressor); 11796 } 11797 break; 11798 } 11799 if (compressor.option("comparisons") && self.is_boolean(compressor)) { 11800 if (parent.TYPE != "Binary") { 11801 var negated = make_node(AST_UnaryPrefix, self, { 11802 operator: "!", 11803 expression: self.negate(compressor), 11804 }); 11805 if (best_of(compressor, self, negated) === negated) return negated; 11806 } 11807 switch (self.operator) { 11808 case ">": reverse("<"); break; 11809 case ">=": reverse("<="); break; 11810 } 11811 } 11812 if (compressor.option("conditionals") && lazy_op[self.operator]) { 11813 if (self.left instanceof AST_Binary && self.operator == self.left.operator) { 11814 var before = make_node(AST_Binary, self, { 11815 operator: self.operator, 11816 left: self.left.right, 11817 right: self.right, 11818 }); 11819 var after = before.transform(compressor); 11820 if (before !== after) { 11821 self.left = self.left.left; 11822 self.right = after; 11823 } 11824 } 11825 // x && (y && z) ---> x && y && z 11826 // w || (x, y || z) ---> w || (x, y) || z 11827 var rhs = self.right.tail_node(); 11828 if (rhs instanceof AST_Binary && self.operator == rhs.operator) swap_chain(self, compressor); 11829 } 11830 if (compressor.option("strings") && self.operator == "+") { 11831 // "foo" + 42 + "" ---> "foo" + 42 11832 if (self.right instanceof AST_String 11833 && self.right.value == "" 11834 && self.left.is_string(compressor)) { 11835 return self.left.optimize(compressor); 11836 } 11837 // "" + ("foo" + 42) ---> "foo" + 42 11838 if (self.left instanceof AST_String 11839 && self.left.value == "" 11840 && self.right.is_string(compressor)) { 11841 return self.right.optimize(compressor); 11842 } 11843 // "" + 42 + "foo" ---> 42 + "foo" 11844 if (self.left instanceof AST_Binary 11845 && self.left.operator == "+" 11846 && self.left.left instanceof AST_String 11847 && self.left.left.value == "" 11848 && self.right.is_string(compressor) 11849 && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) { 11850 self.left = self.left.right; 11851 return self.optimize(compressor); 11852 } 11853 // "x" + (y + "z") ---> "x" + y + "z" 11854 // w + (x, "y" + z) ---> w + (x, "y") + z 11855 var rhs = self.right.tail_node(); 11856 if (rhs instanceof AST_Binary 11857 && self.operator == rhs.operator 11858 && (self.left.is_string(compressor) && rhs.is_string(compressor) 11859 || rhs.left.is_string(compressor) 11860 && (self.left.is_constant() || !rhs.right.has_side_effects(compressor)))) { 11861 swap_chain(self, compressor); 11862 } 11863 } 11864 if (compressor.option("evaluate")) { 11865 var associative = true; 11866 switch (self.operator) { 11867 case "&&": 11868 var ll = fuzzy_eval(compressor, self.left); 11869 if (!ll) { 11870 AST_Node.warn("Condition left of && always false [{start}]", self); 11871 return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor); 11872 } else if (!(ll instanceof AST_Node)) { 11873 AST_Node.warn("Condition left of && always true [{start}]", self); 11874 return make_sequence(self, [ self.left, self.right ]).optimize(compressor); 11875 } 11876 if (!self.right.evaluate(compressor, true)) { 11877 if (in_bool && !(self.right.evaluate(compressor) instanceof AST_Node)) { 11878 AST_Node.warn("Boolean && always false [{start}]", self); 11879 return make_sequence(self, [ self.left, make_node(AST_False, self) ]).optimize(compressor); 11880 } else self.falsy = true; 11881 } else if ((in_bool || parent.operator == "&&" && parent.left === compressor.self()) 11882 && !(self.right.evaluate(compressor) instanceof AST_Node)) { 11883 AST_Node.warn("Dropping side-effect-free && [{start}]", self); 11884 return self.left.optimize(compressor); 11885 } 11886 // (x || false) && y ---> x ? y : false 11887 if (self.left.operator == "||") { 11888 var lr = fuzzy_eval(compressor, self.left.right); 11889 if (!lr) return make_node(AST_Conditional, self, { 11890 condition: self.left.left, 11891 consequent: self.right, 11892 alternative: self.left.right, 11893 }).optimize(compressor); 11894 } 11895 break; 11896 case "??": 11897 var nullish = true; 11898 case "||": 11899 var ll = fuzzy_eval(compressor, self.left, nullish); 11900 if (nullish ? ll == null : !ll) { 11901 AST_Node.warn("Condition left of {operator} always {value} [{start}]", { 11902 operator: self.operator, 11903 value: nullish ? "nullish" : "false", 11904 start: self.start, 11905 }); 11906 return make_sequence(self, [ self.left, self.right ]).optimize(compressor); 11907 } else if (!(ll instanceof AST_Node)) { 11908 AST_Node.warn("Condition left of {operator} always {value} [{start}]", { 11909 operator: self.operator, 11910 value: nullish ? "defined" : "true", 11911 start: self.start, 11912 }); 11913 return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor); 11914 } 11915 var rr; 11916 if (!nullish && (rr = self.right.evaluate(compressor, true)) && !(rr instanceof AST_Node)) { 11917 if (in_bool && !(self.right.evaluate(compressor) instanceof AST_Node)) { 11918 AST_Node.warn("Boolean || always true [{start}]", self); 11919 return make_sequence(self, [ self.left, make_node(AST_True, self) ]).optimize(compressor); 11920 } else self.truthy = true; 11921 } else if ((in_bool || parent.operator == "||" && parent.left === compressor.self()) 11922 && !self.right.evaluate(compressor)) { 11923 AST_Node.warn("Dropping side-effect-free {operator} [{start}]", self); 11924 return self.left.optimize(compressor); 11925 } 11926 // x && true || y ---> x ? true : y 11927 if (!nullish && self.left.operator == "&&") { 11928 var lr = fuzzy_eval(compressor, self.left.right); 11929 if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, { 11930 condition: self.left.left, 11931 consequent: self.left.right, 11932 alternative: self.right, 11933 }).optimize(compressor); 11934 } 11935 break; 11936 case "+": 11937 // "foo" + ("bar" + x) ---> "foobar" + x 11938 if (self.left instanceof AST_Constant 11939 && self.right instanceof AST_Binary 11940 && self.right.operator == "+" 11941 && self.right.left instanceof AST_Constant 11942 && self.right.is_string(compressor)) { 11943 self = make_node(AST_Binary, self, { 11944 operator: "+", 11945 left: make_node(AST_String, self.left, { 11946 value: "" + self.left.value + self.right.left.value, 11947 start: self.left.start, 11948 end: self.right.left.end, 11949 }), 11950 right: self.right.right, 11951 }); 11952 } 11953 // (x + "foo") + "bar" ---> x + "foobar" 11954 if (self.right instanceof AST_Constant 11955 && self.left instanceof AST_Binary 11956 && self.left.operator == "+" 11957 && self.left.right instanceof AST_Constant 11958 && self.left.is_string(compressor)) { 11959 self = make_node(AST_Binary, self, { 11960 operator: "+", 11961 left: self.left.left, 11962 right: make_node(AST_String, self.right, { 11963 value: "" + self.left.right.value + self.right.value, 11964 start: self.left.right.start, 11965 end: self.right.end, 11966 }), 11967 }); 11968 } 11969 // a + -b ---> a - b 11970 if (self.right instanceof AST_UnaryPrefix 11971 && self.right.operator == "-" 11972 && self.left.is_number(compressor)) { 11973 self = make_node(AST_Binary, self, { 11974 operator: "-", 11975 left: self.left, 11976 right: self.right.expression, 11977 }); 11978 break; 11979 } 11980 // -a + b ---> b - a 11981 if (self.left instanceof AST_UnaryPrefix 11982 && self.left.operator == "-" 11983 && reversible() 11984 && self.right.is_number(compressor)) { 11985 self = make_node(AST_Binary, self, { 11986 operator: "-", 11987 left: self.right, 11988 right: self.left.expression, 11989 }); 11990 break; 11991 } 11992 // (a + b) + 3 ---> 3 + (a + b) 11993 if (compressor.option("unsafe_math") 11994 && self.left instanceof AST_Binary 11995 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator] 11996 && self.right.is_constant() 11997 && (self.right.is_boolean(compressor) || self.right.is_number(compressor)) 11998 && self.left.is_number(compressor) 11999 && !self.left.right.is_constant() 12000 && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) { 12001 self = make_node(AST_Binary, self, { 12002 operator: self.left.operator, 12003 left: make_node(AST_Binary, self, { 12004 operator: self.operator, 12005 left: self.right, 12006 right: self.left.left, 12007 }), 12008 right: self.left.right, 12009 }); 12010 break; 12011 } 12012 case "-": 12013 // a - -b ---> a + b 12014 if (self.right instanceof AST_UnaryPrefix 12015 && self.right.operator == "-" 12016 && self.left.is_number(compressor) 12017 && self.right.expression.is_number(compressor)) { 12018 self = make_node(AST_Binary, self, { 12019 operator: "+", 12020 left: self.left, 12021 right: self.right.expression, 12022 }); 12023 break; 12024 } 12025 case "*": 12026 case "/": 12027 associative = compressor.option("unsafe_math"); 12028 // +a - b ---> a - b 12029 // a - +b ---> a - b 12030 if (self.operator != "+") [ "left", "right" ].forEach(function(operand) { 12031 var node = self[operand]; 12032 if (node instanceof AST_UnaryPrefix && node.operator == "+") { 12033 var exp = node.expression; 12034 if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) { 12035 self[operand] = exp; 12036 } 12037 } 12038 }); 12039 case "&": 12040 case "|": 12041 case "^": 12042 // a + +b ---> +b + a 12043 if (self.operator != "-" 12044 && self.operator != "/" 12045 && (self.left.is_boolean(compressor) || self.left.is_number(compressor)) 12046 && (self.right.is_boolean(compressor) || self.right.is_number(compressor)) 12047 && reversible() 12048 && !(self.left instanceof AST_Binary 12049 && self.left.operator != self.operator 12050 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { 12051 self = best_of(compressor, self, make_node(AST_Binary, self, { 12052 operator: self.operator, 12053 left: self.right, 12054 right: self.left, 12055 }), self.right instanceof AST_Constant && !(self.left instanceof AST_Constant)); 12056 } 12057 if (!associative || !self.is_number(compressor)) break; 12058 // a + (b + c) ---> (a + b) + c 12059 if (self.right instanceof AST_Binary 12060 && self.right.operator != "%" 12061 && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator] 12062 && self.right.is_number(compressor) 12063 && (self.operator != "+" 12064 || self.right.left.is_boolean(compressor) 12065 || self.right.left.is_number(compressor)) 12066 && (self.operator != "-" || !self.left.is_negative_zero()) 12067 && (self.right.left.is_constant_expression() 12068 || !self.right.right.has_side_effects(compressor)) 12069 && !is_modify_array(self.right.right)) { 12070 self = make_node(AST_Binary, self, { 12071 operator: align(self.operator, self.right.operator), 12072 left: make_node(AST_Binary, self.left, { 12073 operator: self.operator, 12074 left: self.left, 12075 right: self.right.left, 12076 start: self.left.start, 12077 end: self.right.left.end, 12078 }), 12079 right: self.right.right, 12080 }); 12081 if (self.operator == "+" 12082 && !self.right.is_boolean(compressor) 12083 && !self.right.is_number(compressor)) { 12084 self.right = make_node(AST_UnaryPrefix, self.right, { 12085 operator: "+", 12086 expression: self.right, 12087 }); 12088 } 12089 } 12090 // (2 * n) * 3 ---> 6 * n 12091 // (n + 2) + 3 ---> n + 5 12092 if (self.right instanceof AST_Constant 12093 && self.left instanceof AST_Binary 12094 && self.left.operator != "%" 12095 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator] 12096 && self.left.is_number(compressor)) { 12097 if (self.left.left instanceof AST_Constant) { 12098 var lhs = make_binary(self.operator, self.left.left, self.right, { 12099 start: self.left.left.start, 12100 end: self.right.end, 12101 }); 12102 self = make_binary(self.left.operator, try_evaluate(compressor, lhs), self.left.right, self); 12103 } else if (self.left.right instanceof AST_Constant) { 12104 var op = align(self.left.operator, self.operator); 12105 var rhs = try_evaluate(compressor, make_binary(op, self.left.right, self.right, self.left)); 12106 if (rhs.is_constant() 12107 && !(self.left.operator == "-" 12108 && self.right.value != 0 12109 && +rhs.value == 0 12110 && self.left.left.is_negative_zero())) { 12111 self = make_binary(self.left.operator, self.left.left, rhs, self); 12112 } 12113 } 12114 } 12115 break; 12116 case "instanceof": 12117 if (is_lambda(self.right)) return make_sequence(self, [ 12118 self, 12119 make_node(AST_False, self), 12120 ]).optimize(compressor); 12121 break; 12122 } 12123 if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) { 12124 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) { 12125 // 0 + n ---> n 12126 case "+": 12127 if (self.left.value == 0) { 12128 if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, { 12129 operator: "+", 12130 expression: self.right, 12131 }).optimize(compressor); 12132 if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right; 12133 } 12134 break; 12135 // 1 * n ---> n 12136 case "*": 12137 if (self.left.value == 1) return make_node(AST_UnaryPrefix, self, { 12138 operator: "+", 12139 expression: self.right, 12140 }).optimize(compressor); 12141 break; 12142 } 12143 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) { 12144 // n + 0 ---> n 12145 case "+": 12146 if (self.right.value == 0) { 12147 if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, { 12148 operator: "+", 12149 expression: self.left, 12150 }).optimize(compressor); 12151 if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left; 12152 } 12153 break; 12154 // n - 0 ---> n 12155 case "-": 12156 if (self.right.value == 0) return make_node(AST_UnaryPrefix, self, { 12157 operator: "+", 12158 expression: self.left, 12159 }).optimize(compressor); 12160 break; 12161 // n / 1 ---> n 12162 case "/": 12163 if (self.right.value == 1) return make_node(AST_UnaryPrefix, self, { 12164 operator: "+", 12165 expression: self.left, 12166 }).optimize(compressor); 12167 break; 12168 } 12169 } 12170 } 12171 if (compressor.option("typeofs")) switch (self.operator) { 12172 case "&&": 12173 mark_locally_defined(self.left, self.right, null); 12174 break; 12175 case "||": 12176 mark_locally_defined(self.left, null, self.right); 12177 break; 12178 } 12179 if (compressor.option("unsafe")) { 12180 var indexRight = is_indexFn(self.right); 12181 if (in_bool 12182 && indexRight 12183 && (self.operator == "==" || self.operator == "!=") 12184 && self.left instanceof AST_Number 12185 && self.left.value == 0) { 12186 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, { 12187 operator: "!", 12188 expression: self.right, 12189 }) : self.right).optimize(compressor); 12190 } 12191 var indexLeft = is_indexFn(self.left); 12192 if (compressor.option("comparisons") && is_indexOf_match_pattern()) { 12193 var node = make_node(AST_UnaryPrefix, self, { 12194 operator: "!", 12195 expression: make_node(AST_UnaryPrefix, self, { 12196 operator: "~", 12197 expression: indexLeft ? self.left : self.right, 12198 }), 12199 }); 12200 switch (self.operator) { 12201 case "<": 12202 if (indexLeft) break; 12203 case "<=": 12204 case "!=": 12205 node = make_node(AST_UnaryPrefix, self, { 12206 operator: "!", 12207 expression: node, 12208 }); 12209 break; 12210 } 12211 return node.optimize(compressor); 12212 } 12213 } 12214 return try_evaluate(compressor, self); 12215 12216 function is_modify_array(node) { 12217 var found = false; 12218 node.walk(new TreeWalker(function(node) { 12219 if (found) return true; 12220 if (node instanceof AST_Assign) { 12221 if (node.left instanceof AST_PropAccess) return found = true; 12222 } else if (node instanceof AST_Unary) { 12223 if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) { 12224 return found = true; 12225 } 12226 } 12227 })); 12228 return found; 12229 } 12230 12231 function align(ref, op) { 12232 switch (ref) { 12233 case "-": 12234 return op == "+" ? "-" : "+"; 12235 case "/": 12236 return op == "*" ? "/" : "*"; 12237 default: 12238 return op; 12239 } 12240 } 12241 12242 function make_binary(op, left, right, orig) { 12243 if (op == "+") { 12244 if (!left.is_boolean(compressor) && !left.is_number(compressor)) { 12245 left = make_node(AST_UnaryPrefix, left, { 12246 operator: "+", 12247 expression: left, 12248 }); 12249 } 12250 if (!right.is_boolean(compressor) && !right.is_number(compressor)) { 12251 right = make_node(AST_UnaryPrefix, right, { 12252 operator: "+", 12253 expression: right, 12254 }); 12255 } 12256 } 12257 return make_node(AST_Binary, orig, { 12258 operator: op, 12259 left: left, 12260 right: right, 12261 }); 12262 } 12263 12264 function is_indexFn(node) { 12265 return node.TYPE == "Call" 12266 && node.expression instanceof AST_Dot 12267 && indexFns[node.expression.property]; 12268 } 12269 12270 function is_indexOf_match_pattern() { 12271 switch (self.operator) { 12272 case "<=": 12273 // 0 <= array.indexOf(string) ---> !!~array.indexOf(string) 12274 return indexRight && self.left instanceof AST_Number && self.left.value == 0; 12275 case "<": 12276 // array.indexOf(string) < 0 ---> !~array.indexOf(string) 12277 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true; 12278 // -1 < array.indexOf(string) ---> !!~array.indexOf(string) 12279 case "==": 12280 case "!=": 12281 // -1 == array.indexOf(string) ---> !~array.indexOf(string) 12282 // -1 != array.indexOf(string) ---> !!~array.indexOf(string) 12283 if (!indexRight) return false; 12284 return self.left instanceof AST_Number && self.left.value == -1 12285 || self.left instanceof AST_UnaryPrefix && self.left.operator == "-" 12286 && self.left.expression instanceof AST_Number && self.left.expression.value == 1; 12287 } 12288 } 12289 12290 function reversible() { 12291 return self.left.is_constant() 12292 || self.right.is_constant() 12293 || !self.left.has_side_effects(compressor) 12294 && !self.right.has_side_effects(compressor); 12295 } 12296 12297 function reverse(op) { 12298 if (reversible()) { 12299 if (op) self.operator = op; 12300 var tmp = self.left; 12301 self.left = self.right; 12302 self.right = tmp; 12303 } 12304 } 12305 }); 12306 12307 OPT(AST_SymbolExport, function(self) { 12308 return self; 12309 }); 12310 12311 function recursive_ref(compressor, def, fn) { 12312 var level = 0, node = compressor.self(); 12313 do { 12314 if (node === fn) return node; 12315 if (is_lambda(node) && node.name && node.name.definition() === def) return node; 12316 } while (node = compressor.parent(level++)); 12317 } 12318 12319 function same_scope(def) { 12320 var scope = def.scope.resolve(); 12321 return all(def.references, function(ref) { 12322 return scope === ref.scope.resolve(); 12323 }); 12324 } 12325 12326 OPT(AST_SymbolRef, function(self, compressor) { 12327 if (!compressor.option("ie") 12328 && is_undeclared_ref(self) 12329 // testing against `self.scope.uses_with` is an optimization 12330 && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) { 12331 switch (self.name) { 12332 case "undefined": 12333 return make_node(AST_Undefined, self).optimize(compressor); 12334 case "NaN": 12335 return make_node(AST_NaN, self).optimize(compressor); 12336 case "Infinity": 12337 return make_node(AST_Infinity, self).optimize(compressor); 12338 } 12339 } 12340 var parent = compressor.parent(); 12341 if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) { 12342 var def = self.definition(); 12343 var fixed = self.fixed_value(); 12344 var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor)); 12345 if (single_use) { 12346 if (is_lambda(fixed)) { 12347 if ((def.scope !== self.scope.resolve(true) || def.in_loop) 12348 && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) { 12349 single_use = false; 12350 } else if (def.redefined()) { 12351 single_use = false; 12352 } else if (recursive_ref(compressor, def, fixed)) { 12353 single_use = false; 12354 } else if (fixed.name && fixed.name.definition() !== def) { 12355 single_use = false; 12356 } else if (fixed.parent_scope !== self.scope || is_funarg(def)) { 12357 if (!safe_from_strict_mode(fixed, compressor)) { 12358 single_use = false; 12359 } else if ((single_use = fixed.is_constant_expression(self.scope)) == "f") { 12360 var scope = self.scope; 12361 do { 12362 if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) { 12363 scope.inlined = true; 12364 } 12365 } while (scope = scope.parent_scope); 12366 } 12367 } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed) 12368 || fixed.name.name == "yield" && is_generator(fixed))) { 12369 single_use = false; 12370 } else if (fixed.has_side_effects(compressor)) { 12371 single_use = false; 12372 } else if (compressor.option("ie") && fixed instanceof AST_Class) { 12373 single_use = false; 12374 } 12375 if (single_use) fixed.parent_scope = self.scope; 12376 } else if (!fixed 12377 || def.recursive_refs > 0 12378 || !fixed.is_constant_expression() 12379 || fixed.drop_side_effect_free(compressor)) { 12380 single_use = false; 12381 } 12382 } 12383 if (single_use) { 12384 def.single_use = false; 12385 fixed._squeezed = true; 12386 fixed.single_use = true; 12387 if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed); 12388 if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed); 12389 if (is_lambda(fixed)) { 12390 var scopes = []; 12391 var scope = self.scope; 12392 do { 12393 scopes.push(scope); 12394 if (scope === def.scope) break; 12395 } while (scope = scope.parent_scope); 12396 fixed.enclosed.forEach(function(def) { 12397 if (fixed.variables.has(def.name)) return; 12398 for (var i = 0; i < scopes.length; i++) { 12399 var scope = scopes[i]; 12400 if (!push_uniq(scope.enclosed, def)) return; 12401 scope.var_names().set(def.name, true); 12402 } 12403 }); 12404 } 12405 var value; 12406 if (def.recursive_refs > 0) { 12407 value = fixed.clone(true); 12408 var defun_def = value.name.definition(); 12409 var lambda_def = value.variables.get(value.name.name); 12410 var name = lambda_def && lambda_def.orig[0]; 12411 var def_fn_name, symbol_type; 12412 if (value instanceof AST_Class) { 12413 def_fn_name = "def_function"; 12414 symbol_type = AST_SymbolClass; 12415 } else { 12416 def_fn_name = "def_variable"; 12417 symbol_type = AST_SymbolLambda; 12418 } 12419 if (!(name instanceof symbol_type)) { 12420 name = make_node(symbol_type, value.name); 12421 name.scope = value; 12422 value.name = name; 12423 lambda_def = value[def_fn_name](name); 12424 lambda_def.recursive_refs = def.recursive_refs; 12425 } 12426 value.walk(new TreeWalker(function(node) { 12427 if (node instanceof AST_SymbolDeclaration) { 12428 if (node !== name) { 12429 var def = node.definition(); 12430 def.orig.push(node); 12431 def.eliminated++; 12432 } 12433 return; 12434 } 12435 if (!(node instanceof AST_SymbolRef)) return; 12436 var def = node.definition(); 12437 if (def === defun_def) { 12438 node.thedef = def = lambda_def; 12439 } else { 12440 def.single_use = false; 12441 var fn = node.fixed_value(); 12442 if (is_lambda(fn) 12443 && fn.name 12444 && fn.name.definition() === def 12445 && def.scope === fn.name.scope 12446 && fixed.variables.get(fn.name.name) === def) { 12447 fn.name = fn.name.clone(); 12448 node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name); 12449 } 12450 } 12451 def.references.push(node); 12452 })); 12453 } else { 12454 if (fixed instanceof AST_Scope) { 12455 compressor.push(fixed); 12456 value = fixed.optimize(compressor); 12457 compressor.pop(); 12458 } else { 12459 value = fixed.optimize(compressor); 12460 } 12461 value = value.transform(new TreeTransformer(function(node, descend) { 12462 if (node instanceof AST_Scope) return node; 12463 node = node.clone(); 12464 descend(node, this); 12465 return node; 12466 })); 12467 } 12468 def.replaced++; 12469 return value; 12470 } 12471 var state; 12472 if (fixed && (state = self.fixed || def.fixed).should_replace !== false) { 12473 var ev, init; 12474 if (fixed instanceof AST_This) { 12475 if (!is_funarg(def) && same_scope(def) && !cross_class(def)) init = fixed; 12476 } else if ((ev = fixed.evaluate(compressor, true)) !== fixed 12477 && typeof ev != "function" 12478 && (ev === null 12479 || typeof ev != "object" 12480 || compressor.option("unsafe_regexp") 12481 && ev instanceof RegExp && !def.cross_loop && same_scope(def))) { 12482 init = make_node_from_constant(ev, fixed); 12483 } 12484 if (init) { 12485 if (state.should_replace === undefined) { 12486 var value_length = init.optimize(compressor).print_to_string().length; 12487 if (!has_symbol_ref(fixed)) { 12488 value_length = Math.min(value_length, fixed.print_to_string().length); 12489 } 12490 var name_length = def.name.length; 12491 if (compressor.option("unused") && !compressor.exposed(def)) { 12492 var refs = def.references.length - def.replaced - def.assignments; 12493 refs = Math.min(refs, def.references.filter(function(ref) { 12494 return ref.fixed === state; 12495 }).length); 12496 name_length += (name_length + 2 + value_length) / Math.max(1, refs); 12497 } 12498 state.should_replace = value_length - Math.floor(name_length) < compressor.eval_threshold; 12499 } 12500 if (state.should_replace) { 12501 var value; 12502 if (has_symbol_ref(fixed)) { 12503 value = init.optimize(compressor); 12504 if (value === init) value = value.clone(true); 12505 } else { 12506 value = best_of_expression(init.optimize(compressor), fixed); 12507 if (value === init || value === fixed) value = value.clone(true); 12508 } 12509 def.replaced++; 12510 return value; 12511 } 12512 } 12513 } 12514 } 12515 return self; 12516 12517 function cross_class(def) { 12518 var scope = self.scope; 12519 while (scope !== def.scope) { 12520 if (scope instanceof AST_Class) return true; 12521 scope = scope.parent_scope; 12522 } 12523 } 12524 12525 function has_symbol_ref(value) { 12526 var found; 12527 value.walk(new TreeWalker(function(node) { 12528 if (node instanceof AST_SymbolRef) found = true; 12529 if (found) return true; 12530 })); 12531 return found; 12532 } 12533 }); 12534 12535 function is_raw_tag(compressor, tag) { 12536 return compressor.option("unsafe") 12537 && tag instanceof AST_Dot 12538 && tag.property == "raw" 12539 && is_undeclared_ref(tag.expression) 12540 && tag.expression.name == "String"; 12541 } 12542 12543 function decode_template(str) { 12544 var malformed = false; 12545 str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) { 12546 var ch = decode_escape_sequence(seq); 12547 if (typeof ch == "string") return ch; 12548 malformed = true; 12549 }); 12550 if (!malformed) return str; 12551 } 12552 12553 OPT(AST_Template, function(self, compressor) { 12554 if (!compressor.option("templates")) return self; 12555 var tag = self.tag; 12556 if (!tag || is_raw_tag(compressor, tag)) { 12557 var exprs = []; 12558 var strs = []; 12559 for (var i = 0, status; i < self.strings.length; i++) { 12560 var str = self.strings[i]; 12561 if (!tag) { 12562 var trimmed = decode_template(str); 12563 if (trimmed) str = escape_literal(trimmed); 12564 } 12565 if (i > 0) { 12566 var node = self.expressions[i - 1]; 12567 var value = should_join(node); 12568 if (value) { 12569 var prev = strs[strs.length - 1]; 12570 var joined = prev + value + str; 12571 var decoded; 12572 if (tag || typeof (decoded = decode_template(joined)) == status) { 12573 strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined; 12574 continue; 12575 } 12576 } 12577 exprs.push(node); 12578 } 12579 strs.push(str); 12580 if (!tag) status = typeof trimmed; 12581 } 12582 if (!tag && strs.length > 1) { 12583 if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, { 12584 operator: "+", 12585 left: make_node(AST_Template, self, { 12586 expressions: exprs.slice(0, -1), 12587 strings: strs.slice(0, -1), 12588 }).transform(compressor), 12589 right: exprs[exprs.length - 1], 12590 }).optimize(compressor); 12591 if (strs[0] == "") { 12592 var left = make_node(AST_Binary, self, { 12593 operator: "+", 12594 left: make_node(AST_String, self, { value: "" }), 12595 right: exprs[0], 12596 }); 12597 for (var i = 1; strs[i] == "" && i < exprs.length; i++) { 12598 left = make_node(AST_Binary, self, { 12599 operator: "+", 12600 left: left, 12601 right: exprs[i], 12602 }); 12603 } 12604 return best_of(compressor, self, make_node(AST_Binary, self, { 12605 operator: "+", 12606 left: left.transform(compressor), 12607 right: make_node(AST_Template, self, { 12608 expressions: exprs.slice(i), 12609 strings: strs.slice(i), 12610 }).transform(compressor), 12611 }).optimize(compressor)); 12612 } 12613 } 12614 self.expressions = exprs; 12615 self.strings = strs; 12616 } 12617 return try_evaluate(compressor, self); 12618 12619 function escape_literal(str) { 12620 return str.replace(/\r|\\|`|\${/g, function(s) { 12621 return "\\" + (s == "\r" ? "r" : s); 12622 }); 12623 } 12624 12625 function should_join(node) { 12626 var ev = node.evaluate(compressor); 12627 if (ev === node) return; 12628 if (tag && /\r|\\|`/.test(ev)) return; 12629 ev = escape_literal("" + ev); 12630 if (ev.length > node.print_to_string().length + "${}".length) return; 12631 return ev; 12632 } 12633 }); 12634 12635 function is_atomic(lhs, self) { 12636 return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE; 12637 } 12638 12639 OPT(AST_Undefined, function(self, compressor) { 12640 if (compressor.option("unsafe_undefined")) { 12641 var undef = find_scope(compressor).find_variable("undefined"); 12642 if (undef) { 12643 var ref = make_node(AST_SymbolRef, self, { 12644 name: "undefined", 12645 scope: undef.scope, 12646 thedef: undef, 12647 }); 12648 ref.is_undefined = true; 12649 return ref; 12650 } 12651 } 12652 var lhs = is_lhs(compressor.self(), compressor.parent()); 12653 if (lhs && is_atomic(lhs, self)) return self; 12654 return make_node(AST_UnaryPrefix, self, { 12655 operator: "void", 12656 expression: make_node(AST_Number, self, { value: 0 }), 12657 }); 12658 }); 12659 12660 OPT(AST_Infinity, function(self, compressor) { 12661 var lhs = is_lhs(compressor.self(), compressor.parent()); 12662 if (lhs && is_atomic(lhs, self)) return self; 12663 if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) { 12664 return self; 12665 } 12666 return make_node(AST_Binary, self, { 12667 operator: "/", 12668 left: make_node(AST_Number, self, { value: 1 }), 12669 right: make_node(AST_Number, self, { value: 0 }), 12670 }); 12671 }); 12672 12673 OPT(AST_NaN, function(self, compressor) { 12674 var lhs = is_lhs(compressor.self(), compressor.parent()); 12675 if (lhs && is_atomic(lhs, self)) return self; 12676 if (!lhs && !find_scope(compressor).find_variable("NaN")) return self; 12677 return make_node(AST_Binary, self, { 12678 operator: "/", 12679 left: make_node(AST_Number, self, { value: 0 }), 12680 right: make_node(AST_Number, self, { value: 0 }), 12681 }); 12682 }); 12683 12684 function is_reachable(self, defs) { 12685 var reachable = false; 12686 var find_ref = new TreeWalker(function(node) { 12687 if (reachable) return true; 12688 if (node instanceof AST_SymbolRef && member(node.definition(), defs)) return reachable = true; 12689 }); 12690 var scan_scope = new TreeWalker(function(node) { 12691 if (reachable) return true; 12692 if (node instanceof AST_Lambda && node !== self) { 12693 if (!(node.name || is_async(node) || is_generator(node))) { 12694 var parent = scan_scope.parent(); 12695 if (parent instanceof AST_Call && parent.expression === node) return; 12696 } 12697 node.walk(find_ref); 12698 return true; 12699 } 12700 }); 12701 self.walk(scan_scope); 12702 return reachable; 12703 } 12704 12705 var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &"); 12706 var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &"); 12707 OPT(AST_Assign, function(self, compressor) { 12708 if (compressor.option("dead_code")) { 12709 if (self.left instanceof AST_PropAccess) { 12710 if (self.operator == "=") { 12711 if (self.redundant) { 12712 var exprs = [ self.left.expression ]; 12713 if (self.left instanceof AST_Sub) exprs.push(self.left.property); 12714 exprs.push(self.right); 12715 return make_sequence(self, exprs).optimize(compressor); 12716 } 12717 if (self.left.equals(self.right) && !self.left.has_side_effects(compressor)) { 12718 return self.right; 12719 } 12720 var exp = self.left.expression; 12721 if (exp instanceof AST_Lambda 12722 || !compressor.has_directive("use strict") 12723 && exp instanceof AST_Constant 12724 && !exp.may_throw_on_access(compressor)) { 12725 return self.left instanceof AST_Dot ? self.right : make_sequence(self, [ 12726 self.left.property, 12727 self.right 12728 ]).optimize(compressor); 12729 } 12730 } 12731 } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) { 12732 var parent; 12733 if (self.operator == "=" && self.left.equals(self.right) 12734 && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) { 12735 return self.right; 12736 } 12737 if (self.left.is_immutable()) return strip_assignment(); 12738 var def = self.left.definition(); 12739 var scope = def.scope.resolve(); 12740 var local = scope === compressor.find_parent(AST_Lambda); 12741 var level = 0, node; 12742 parent = compressor.self(); 12743 if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do { 12744 node = parent; 12745 parent = compressor.parent(level++); 12746 if (parent instanceof AST_Assign) { 12747 if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) { 12748 if (in_try(level, parent, !local)) break; 12749 return strip_assignment(def); 12750 } 12751 if (parent.left.match_symbol(function(node) { 12752 if (node instanceof AST_PropAccess) return true; 12753 })) break; 12754 continue; 12755 } 12756 if (parent instanceof AST_Exit) { 12757 if (!local) break; 12758 if (in_try(level, parent)) break; 12759 if (is_reachable(scope, [ def ])) break; 12760 return strip_assignment(def); 12761 } 12762 if (parent instanceof AST_SimpleStatement) { 12763 if (!local) break; 12764 if (is_reachable(scope, [ def ])) break; 12765 var stat; 12766 do { 12767 stat = parent; 12768 parent = compressor.parent(level++); 12769 if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def); 12770 } while (is_tail_block(stat, parent)); 12771 break; 12772 } 12773 if (parent instanceof AST_VarDef) { 12774 if (!(parent.name instanceof AST_SymbolDeclaration)) continue; 12775 if (parent.name.definition() !== def) continue; 12776 if (in_try(level, parent)) break; 12777 return strip_assignment(def); 12778 } 12779 } while (is_tail(node, parent)); 12780 } 12781 } 12782 if (compressor.option("sequences")) { 12783 var seq = self.lift_sequences(compressor); 12784 if (seq !== self) return seq.optimize(compressor); 12785 } 12786 if (compressor.option("assignments")) { 12787 if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) { 12788 // x = expr1 OP expr2 12789 if (self.right.left instanceof AST_SymbolRef 12790 && self.right.left.name == self.left.name 12791 && ASSIGN_OPS[self.right.operator]) { 12792 // x = x - 2 ---> x -= 2 12793 return make_compound(self.right.right); 12794 } 12795 if (self.right.right instanceof AST_SymbolRef 12796 && self.right.right.name == self.left.name 12797 && ASSIGN_OPS_COMMUTATIVE[self.right.operator] 12798 && !self.right.left.has_side_effects(compressor)) { 12799 // x = 2 & x ---> x &= 2 12800 return make_compound(self.right.left); 12801 } 12802 } 12803 if ((self.operator == "-=" || self.operator == "+=" 12804 && (self.left.is_boolean(compressor) || self.left.is_number(compressor))) 12805 && self.right instanceof AST_Number 12806 && self.right.value == 1) { 12807 var op = self.operator.slice(0, -1); 12808 return make_node(AST_UnaryPrefix, self, { 12809 operator: op + op, 12810 expression: self.left, 12811 }); 12812 } 12813 } 12814 return try_evaluate(compressor, self); 12815 12816 function is_tail(node, parent) { 12817 if (parent instanceof AST_Binary) switch (node) { 12818 case parent.left: 12819 return parent.right.is_constant_expression(scope); 12820 case parent.right: 12821 return true; 12822 default: 12823 return false; 12824 } 12825 if (parent instanceof AST_Conditional) switch (node) { 12826 case parent.condition: 12827 return parent.consequent.is_constant_expression(scope) 12828 && parent.alternative.is_constant_expression(scope); 12829 case parent.consequent: 12830 case parent.alternative: 12831 return true; 12832 default: 12833 return false; 12834 } 12835 if (parent instanceof AST_Sequence) { 12836 var exprs = parent.expressions; 12837 var stop = exprs.indexOf(node); 12838 if (stop < 0) return false; 12839 for (var i = exprs.length; --i > stop;) { 12840 if (!exprs[i].is_constant_expression(scope)) return false; 12841 } 12842 return true; 12843 } 12844 return parent instanceof AST_UnaryPrefix; 12845 } 12846 12847 function is_tail_block(stat, parent) { 12848 if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat); 12849 if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat); 12850 if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat); 12851 if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat; 12852 if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat; 12853 } 12854 12855 function in_try(level, node, sync) { 12856 var right = self.right; 12857 self.right = make_node(AST_Null, right); 12858 var may_throw = node.may_throw(compressor); 12859 self.right = right; 12860 return find_try(compressor, level, node, scope, may_throw, sync); 12861 } 12862 12863 function make_compound(rhs) { 12864 var fixed = self.left.fixed; 12865 if (fixed) fixed.to_binary = replace_ref(function(node) { 12866 return node.left; 12867 }, fixed); 12868 return make_node(AST_Assign, self, { 12869 operator: self.right.operator + "=", 12870 left: self.left, 12871 right: rhs, 12872 }); 12873 } 12874 12875 function strip_assignment(def) { 12876 if (def) def.fixed = false; 12877 return (self.operator != "=" ? make_node(AST_Binary, self, { 12878 operator: self.operator.slice(0, -1), 12879 left: self.left, 12880 right: self.right, 12881 }) : maintain_this_binding(compressor.parent(), self, self.right)).optimize(compressor); 12882 } 12883 }); 12884 12885 OPT(AST_Conditional, function(self, compressor) { 12886 if (compressor.option("sequences") && self.condition instanceof AST_Sequence) { 12887 var expressions = self.condition.expressions.slice(); 12888 var node = self.clone(); 12889 node.condition = expressions.pop(); 12890 expressions.push(node); 12891 return make_sequence(self, expressions).optimize(compressor); 12892 } 12893 if (!compressor.option("conditionals")) return self; 12894 var condition = self.condition; 12895 if (compressor.option("booleans") && !condition.has_side_effects(compressor)) { 12896 mark_duplicate_condition(compressor, condition); 12897 } 12898 condition = fuzzy_eval(compressor, condition); 12899 if (!condition) { 12900 AST_Node.warn("Condition always false [{start}]", self); 12901 return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor); 12902 } else if (!(condition instanceof AST_Node)) { 12903 AST_Node.warn("Condition always true [{start}]", self); 12904 return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor); 12905 } 12906 var first = first_in_statement(compressor); 12907 var negated = condition.negate(compressor, first); 12908 if ((first ? best_of_statement : best_of_expression)(condition, negated) === negated) { 12909 self = make_node(AST_Conditional, self, { 12910 condition: negated, 12911 consequent: self.alternative, 12912 alternative: self.consequent, 12913 }); 12914 negated = condition; 12915 condition = self.condition; 12916 } 12917 var consequent = self.consequent; 12918 var alternative = self.alternative; 12919 var cond_lhs = extract_lhs(condition, compressor); 12920 if (repeatable(compressor, cond_lhs)) { 12921 // x ? x : y ---> x || y 12922 if (cond_lhs.equals(consequent)) return make_node(AST_Binary, self, { 12923 operator: "||", 12924 left: condition, 12925 right: alternative, 12926 }).optimize(compressor); 12927 // x ? y : x ---> x && y 12928 if (cond_lhs.equals(alternative)) return make_node(AST_Binary, self, { 12929 operator: "&&", 12930 left: condition, 12931 right: consequent, 12932 }).optimize(compressor); 12933 } 12934 // if (foo) exp = something; else exp = something_else; 12935 // | 12936 // v 12937 // exp = foo ? something : something_else; 12938 var seq_tail = consequent.tail_node(); 12939 if (seq_tail instanceof AST_Assign) { 12940 var is_eq = seq_tail.operator == "="; 12941 var alt_tail = is_eq ? alternative.tail_node() : alternative; 12942 if ((is_eq || consequent === seq_tail) 12943 && alt_tail instanceof AST_Assign 12944 && seq_tail.operator == alt_tail.operator 12945 && seq_tail.left.equals(alt_tail.left) 12946 && (is_eq && seq_tail.left instanceof AST_SymbolRef 12947 || !condition.has_side_effects(compressor) 12948 && can_shift_lhs_of_tail(consequent) 12949 && can_shift_lhs_of_tail(alternative))) { 12950 return make_node(AST_Assign, self, { 12951 operator: seq_tail.operator, 12952 left: seq_tail.left, 12953 right: make_node(AST_Conditional, self, { 12954 condition: condition, 12955 consequent: pop_lhs(consequent), 12956 alternative: pop_lhs(alternative), 12957 }), 12958 }); 12959 } 12960 } 12961 var alt_tail = alternative.tail_node(); 12962 // x ? y : y ---> x, y 12963 // x ? (a, c) : (b, c) ---> x ? a : b, c 12964 if (seq_tail.equals(alt_tail)) return make_sequence(self, consequent.equals(alternative) ? [ 12965 condition, 12966 consequent, 12967 ] : [ 12968 make_node(AST_Conditional, self, { 12969 condition: condition, 12970 consequent: pop_seq(consequent), 12971 alternative: pop_seq(alternative), 12972 }), 12973 alt_tail, 12974 ]).optimize(compressor); 12975 // x ? y.p : z.p ---> (x ? y : z).p 12976 // x ? y(a) : z(a) ---> (x ? y : z)(a) 12977 // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a) 12978 var combined = combine_tail(consequent, alternative, true); 12979 if (combined) return combined; 12980 // x ? y(a) : y(b) ---> y(x ? a : b) 12981 var arg_index; 12982 if (consequent instanceof AST_Call 12983 && alternative.TYPE == consequent.TYPE 12984 && (arg_index = arg_diff(consequent, alternative)) >= 0 12985 && consequent.expression.equals(alternative.expression) 12986 && !condition.has_side_effects(compressor) 12987 && !consequent.expression.has_side_effects(compressor)) { 12988 var node = consequent.clone(); 12989 var arg = consequent.args[arg_index]; 12990 node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, { 12991 expression: make_node(AST_Conditional, self, { 12992 condition: condition, 12993 consequent: arg.expression, 12994 alternative: alternative.args[arg_index].expression, 12995 }), 12996 }) : make_node(AST_Conditional, self, { 12997 condition: condition, 12998 consequent: arg, 12999 alternative: alternative.args[arg_index], 13000 }); 13001 return node; 13002 } 13003 // x ? (y ? a : b) : b ---> x && y ? a : b 13004 if (seq_tail instanceof AST_Conditional 13005 && seq_tail.alternative.equals(alternative)) { 13006 return make_node(AST_Conditional, self, { 13007 condition: make_node(AST_Binary, self, { 13008 left: condition, 13009 operator: "&&", 13010 right: fuse(consequent, seq_tail, "condition"), 13011 }), 13012 consequent: seq_tail.consequent, 13013 alternative: merge_expression(seq_tail.alternative, alternative), 13014 }); 13015 } 13016 // x ? (y ? a : b) : a ---> !x || y ? a : b 13017 if (seq_tail instanceof AST_Conditional 13018 && seq_tail.consequent.equals(alternative)) { 13019 return make_node(AST_Conditional, self, { 13020 condition: make_node(AST_Binary, self, { 13021 left: negated, 13022 operator: "||", 13023 right: fuse(consequent, seq_tail, "condition"), 13024 }), 13025 consequent: merge_expression(seq_tail.consequent, alternative), 13026 alternative: seq_tail.alternative, 13027 }); 13028 } 13029 // x ? a : (y ? a : b) ---> x || y ? a : b 13030 if (alt_tail instanceof AST_Conditional 13031 && consequent.equals(alt_tail.consequent)) { 13032 return make_node(AST_Conditional, self, { 13033 condition: make_node(AST_Binary, self, { 13034 left: condition, 13035 operator: "||", 13036 right: fuse(alternative, alt_tail, "condition"), 13037 }), 13038 consequent: merge_expression(consequent, alt_tail.consequent), 13039 alternative: alt_tail.alternative, 13040 }); 13041 } 13042 // x ? b : (y ? a : b) ---> !x && y ? a : b 13043 if (alt_tail instanceof AST_Conditional 13044 && consequent.equals(alt_tail.alternative)) { 13045 return make_node(AST_Conditional, self, { 13046 condition: make_node(AST_Binary, self, { 13047 left: negated, 13048 operator: "&&", 13049 right: fuse(alternative, alt_tail, "condition"), 13050 }), 13051 consequent: alt_tail.consequent, 13052 alternative: merge_expression(consequent, alt_tail.alternative), 13053 }); 13054 } 13055 // x ? y && a : a ---> (!x || y) && a 13056 if (seq_tail instanceof AST_Binary 13057 && seq_tail.operator == "&&" 13058 && seq_tail.right.equals(alternative)) { 13059 return make_node(AST_Binary, self, { 13060 operator: "&&", 13061 left: make_node(AST_Binary, self, { 13062 operator: "||", 13063 left: negated, 13064 right: fuse(consequent, seq_tail, "left"), 13065 }), 13066 right: merge_expression(seq_tail.right, alternative), 13067 }).optimize(compressor); 13068 } 13069 // x ? y || a : a ---> x && y || a 13070 if (seq_tail instanceof AST_Binary 13071 && seq_tail.operator == "||" 13072 && seq_tail.right.equals(alternative)) { 13073 return make_node(AST_Binary, self, { 13074 operator: "||", 13075 left: make_node(AST_Binary, self, { 13076 operator: "&&", 13077 left: condition, 13078 right: fuse(consequent, seq_tail, "left"), 13079 }), 13080 right: merge_expression(seq_tail.right, alternative), 13081 }).optimize(compressor); 13082 } 13083 // x ? a : y && a ---> (x || y) && a 13084 if (alt_tail instanceof AST_Binary 13085 && alt_tail.operator == "&&" 13086 && alt_tail.right.equals(consequent)) { 13087 return make_node(AST_Binary, self, { 13088 operator: "&&", 13089 left: make_node(AST_Binary, self, { 13090 operator: "||", 13091 left: condition, 13092 right: fuse(alternative, alt_tail, "left"), 13093 }), 13094 right: merge_expression(consequent, alt_tail.right), 13095 }).optimize(compressor); 13096 } 13097 // x ? a : y || a ---> !x && y || a 13098 if (alt_tail instanceof AST_Binary 13099 && alt_tail.operator == "||" 13100 && alt_tail.right.equals(consequent)) { 13101 return make_node(AST_Binary, self, { 13102 operator: "||", 13103 left: make_node(AST_Binary, self, { 13104 operator: "&&", 13105 left: negated, 13106 right: fuse(alternative, alt_tail, "left"), 13107 }), 13108 right: merge_expression(consequent, alt_tail.right), 13109 }).optimize(compressor); 13110 } 13111 var in_bool = compressor.option("booleans") && compressor.in_boolean_context(); 13112 if (is_true(consequent)) { 13113 // c ? true : false ---> !!c 13114 if (is_false(alternative)) return booleanize(condition); 13115 // c ? true : x ---> !!c || x 13116 return make_node(AST_Binary, self, { 13117 operator: "||", 13118 left: booleanize(condition), 13119 right: alternative, 13120 }).optimize(compressor); 13121 } 13122 if (is_false(consequent)) { 13123 // c ? false : true ---> !c 13124 if (is_true(alternative)) return booleanize(condition.negate(compressor)); 13125 // c ? false : x ---> !c && x 13126 return make_node(AST_Binary, self, { 13127 operator: "&&", 13128 left: booleanize(condition.negate(compressor)), 13129 right: alternative, 13130 }).optimize(compressor); 13131 } 13132 // c ? x : true ---> !c || x 13133 if (is_true(alternative)) return make_node(AST_Binary, self, { 13134 operator: "||", 13135 left: booleanize(condition.negate(compressor)), 13136 right: consequent, 13137 }).optimize(compressor); 13138 // c ? x : false ---> !!c && x 13139 if (is_false(alternative)) return make_node(AST_Binary, self, { 13140 operator: "&&", 13141 left: booleanize(condition), 13142 right: consequent, 13143 }).optimize(compressor); 13144 if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative); 13145 return self; 13146 13147 function booleanize(node) { 13148 if (node.is_boolean(compressor)) return node; 13149 // !!expression 13150 return make_node(AST_UnaryPrefix, node, { 13151 operator: "!", 13152 expression: node.negate(compressor), 13153 }); 13154 } 13155 13156 // AST_True or !0 13157 function is_true(node) { 13158 return node instanceof AST_True 13159 || in_bool 13160 && node instanceof AST_Constant 13161 && node.value 13162 || (node instanceof AST_UnaryPrefix 13163 && node.operator == "!" 13164 && node.expression instanceof AST_Constant 13165 && !node.expression.value); 13166 } 13167 // AST_False or !1 or void 0 13168 function is_false(node) { 13169 return node instanceof AST_False 13170 || in_bool 13171 && (node instanceof AST_Constant 13172 && !node.value 13173 || node instanceof AST_UnaryPrefix 13174 && node.operator == "void" 13175 && !node.expression.has_side_effects(compressor)) 13176 || (node instanceof AST_UnaryPrefix 13177 && node.operator == "!" 13178 && node.expression instanceof AST_Constant 13179 && node.expression.value); 13180 } 13181 13182 function arg_diff(consequent, alternative) { 13183 var a = consequent.args; 13184 var b = alternative.args; 13185 var len = a.length; 13186 if (len != b.length) return -2; 13187 for (var i = 0; i < len; i++) { 13188 if (!a[i].equals(b[i])) { 13189 if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3; 13190 for (var j = i + 1; j < len; j++) { 13191 if (!a[j].equals(b[j])) return -2; 13192 } 13193 return i; 13194 } 13195 } 13196 return -1; 13197 } 13198 13199 function fuse(node, tail, prop) { 13200 if (node === tail) return tail[prop]; 13201 var exprs = node.expressions.slice(0, -1); 13202 exprs.push(tail[prop]); 13203 return make_sequence(node, exprs); 13204 } 13205 13206 function is_tail_equivalent(consequent, alternative) { 13207 if (consequent.TYPE != alternative.TYPE) return; 13208 if (consequent.optional != alternative.optional) return; 13209 if (consequent instanceof AST_Call) { 13210 if (arg_diff(consequent, alternative) != -1) return; 13211 return consequent.TYPE != "Call" 13212 || !(consequent.expression instanceof AST_PropAccess 13213 || alternative.expression instanceof AST_PropAccess) 13214 || is_tail_equivalent(consequent.expression, alternative.expression); 13215 } 13216 if (!(consequent instanceof AST_PropAccess)) return; 13217 var p = consequent.property; 13218 var q = alternative.property; 13219 return (p instanceof AST_Node ? p.equals(q) : p == q) 13220 && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super); 13221 } 13222 13223 function combine_tail(consequent, alternative, top) { 13224 var seq_tail = consequent.tail_node(); 13225 var alt_tail = alternative.tail_node(); 13226 if (!is_tail_equivalent(seq_tail, alt_tail)) return !top && make_node(AST_Conditional, self, { 13227 condition: condition, 13228 consequent: consequent, 13229 alternative: alternative, 13230 }); 13231 var node = seq_tail.clone(); 13232 var seq_expr = fuse(consequent, seq_tail, "expression"); 13233 var alt_expr = fuse(alternative, alt_tail, "expression"); 13234 var combined = combine_tail(seq_expr, alt_expr); 13235 if (seq_tail.expression instanceof AST_Sequence) { 13236 combined = maintain_this_binding(seq_tail, seq_tail.expression, combined); 13237 } 13238 node.expression = combined; 13239 return node; 13240 } 13241 13242 function can_shift_lhs_of_tail(node) { 13243 return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) { 13244 return !expr.has_side_effects(compressor); 13245 }); 13246 } 13247 13248 function pop_lhs(node) { 13249 if (!(node instanceof AST_Sequence)) return node.right; 13250 var exprs = node.expressions.slice(); 13251 exprs.push(exprs.pop().right); 13252 return make_sequence(node, exprs); 13253 } 13254 13255 function pop_seq(node) { 13256 if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { value: 0 }); 13257 return make_sequence(node, node.expressions.slice(0, -1)); 13258 } 13259 }); 13260 13261 OPT(AST_Boolean, function(self, compressor) { 13262 if (!compressor.option("booleans")) return self; 13263 if (compressor.in_boolean_context()) return make_node(AST_Number, self, { value: +self.value }); 13264 var p = compressor.parent(); 13265 if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) { 13266 AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{start}]", { 13267 operator: p.operator, 13268 value: self.value, 13269 start: p.start, 13270 }); 13271 return make_node(AST_Number, self, { value: +self.value }); 13272 } 13273 return make_node(AST_UnaryPrefix, self, { 13274 operator: "!", 13275 expression: make_node(AST_Number, self, { value: 1 - self.value }), 13276 }); 13277 }); 13278 13279 OPT(AST_Spread, function(self, compressor) { 13280 var exp = self.expression; 13281 if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) { 13282 return List.splice(exp.elements.map(function(node) { 13283 return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node; 13284 })); 13285 } 13286 return self; 13287 }); 13288 13289 function safe_to_flatten(value, compressor) { 13290 if (!value) return false; 13291 var parent = compressor.parent(); 13292 if (parent.TYPE != "Call") return true; 13293 if (parent.expression !== compressor.self()) return true; 13294 if (value instanceof AST_SymbolRef) { 13295 value = value.fixed_value(); 13296 if (!value) return false; 13297 } 13298 return value instanceof AST_Lambda && !value.contains_this(); 13299 } 13300 13301 OPT(AST_Sub, function(self, compressor) { 13302 var expr = self.expression; 13303 var prop = self.property; 13304 var terminated = trim_optional_chain(self, compressor); 13305 if (terminated) return terminated; 13306 if (compressor.option("properties")) { 13307 var key = prop.evaluate(compressor); 13308 if (key !== prop) { 13309 if (typeof key == "string") { 13310 if (key == "undefined") { 13311 key = undefined; 13312 } else { 13313 var value = parseFloat(key); 13314 if (value.toString() == key) { 13315 key = value; 13316 } 13317 } 13318 } 13319 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor)); 13320 var property = "" + key; 13321 if (is_identifier_string(property) 13322 && property.length <= prop.print_to_string().length + 1) { 13323 return make_node(AST_Dot, self, { 13324 optional: self.optional, 13325 expression: expr, 13326 property: property, 13327 quoted: true, 13328 }).optimize(compressor); 13329 } 13330 } 13331 } 13332 var parent = compressor.parent(); 13333 var assigned = is_lhs(compressor.self(), parent); 13334 var def, fn, fn_parent, index; 13335 if (compressor.option("arguments") 13336 && expr instanceof AST_SymbolRef 13337 && is_arguments(def = expr.definition()) 13338 && !expr.in_arg 13339 && prop instanceof AST_Number 13340 && Math.floor(index = prop.value) == index 13341 && (fn = def.scope) === find_lambda() 13342 && fn.uses_arguments < (assigned ? 2 : 3)) { 13343 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") { 13344 if (!def.deleted) def.deleted = []; 13345 def.deleted[index] = true; 13346 } 13347 var argname = fn.argnames[index]; 13348 if (def.deleted && def.deleted[index]) { 13349 argname = null; 13350 } else if (argname) { 13351 var arg_def; 13352 if (!(argname instanceof AST_SymbolFunarg) 13353 || argname.name == "await" 13354 || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) { 13355 argname = null; 13356 } else if (compressor.has_directive("use strict") 13357 || fn.name 13358 || fn.rest 13359 || !(fn_parent instanceof AST_Call 13360 && index < fn_parent.args.length 13361 && all(fn_parent.args.slice(0, index + 1), function(arg) { 13362 return !(arg instanceof AST_Spread); 13363 })) 13364 || !all(fn.argnames, function(argname) { 13365 return argname instanceof AST_SymbolFunarg; 13366 })) { 13367 if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null; 13368 } 13369 } else if ((assigned || !has_reassigned()) 13370 && index < fn.argnames.length + 5 13371 && compressor.drop_fargs(fn, fn_parent) 13372 && !fn.rest) { 13373 while (index >= fn.argnames.length) { 13374 argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length); 13375 fn.argnames.push(argname); 13376 } 13377 } 13378 if (argname && find_if(function(node) { 13379 return node.name === argname.name; 13380 }, fn.argnames) === argname) { 13381 if (assigned) def.reassigned--; 13382 var sym = make_node(AST_SymbolRef, argname); 13383 sym.reference(); 13384 argname.unused = undefined; 13385 return sym; 13386 } 13387 } 13388 if (assigned) return self; 13389 if (compressor.option("sequences") 13390 && parent.TYPE != "Call" 13391 && !(parent instanceof AST_ForEnumeration && parent.init === self)) { 13392 var seq = lift_sequence_in_expression(self, compressor); 13393 if (seq !== self) return seq.optimize(compressor); 13394 } 13395 if (key !== prop) { 13396 var sub = self.flatten_object(property, compressor); 13397 if (sub) { 13398 expr = self.expression = sub.expression; 13399 prop = self.property = sub.property; 13400 } 13401 } 13402 var elements; 13403 if (compressor.option("properties") 13404 && compressor.option("side_effects") 13405 && prop instanceof AST_Number 13406 && expr instanceof AST_Array 13407 && all(elements = expr.elements, function(value) { 13408 return !(value instanceof AST_Spread); 13409 })) { 13410 var index = prop.value; 13411 var retValue = elements[index]; 13412 if (safe_to_flatten(retValue, compressor)) { 13413 var is_hole = retValue instanceof AST_Hole; 13414 var flatten = !is_hole; 13415 var values = []; 13416 for (var i = elements.length; --i > index;) { 13417 var value = elements[i].drop_side_effect_free(compressor); 13418 if (value) { 13419 values.unshift(value); 13420 if (flatten && value.has_side_effects(compressor)) flatten = false; 13421 } 13422 } 13423 if (!flatten) values.unshift(retValue); 13424 while (--i >= 0) { 13425 var value = elements[i].drop_side_effect_free(compressor); 13426 if (value) { 13427 values.unshift(value); 13428 } else if (is_hole) { 13429 values.unshift(make_node(AST_Hole, elements[i])); 13430 } else { 13431 index--; 13432 } 13433 } 13434 if (flatten) { 13435 values.push(retValue); 13436 return make_sequence(self, values).optimize(compressor); 13437 } 13438 return make_node(AST_Sub, self, { 13439 expression: make_node(AST_Array, expr, { elements: values }), 13440 property: make_node(AST_Number, prop, { value: index }), 13441 }); 13442 } 13443 } 13444 return try_evaluate(compressor, self); 13445 13446 function find_lambda() { 13447 var i = 0, p; 13448 while (p = compressor.parent(i++)) { 13449 if (p instanceof AST_Lambda) { 13450 if (p instanceof AST_Accessor) return; 13451 if (is_arrow(p)) continue; 13452 fn_parent = compressor.parent(i); 13453 return p; 13454 } 13455 } 13456 } 13457 13458 function has_reassigned() { 13459 return !compressor.option("reduce_vars") || def.reassigned; 13460 } 13461 }); 13462 13463 AST_LambdaExpression.DEFMETHOD("contains_super", function() { 13464 var result = false; 13465 var self = this; 13466 self.walk(new TreeWalker(function(node) { 13467 if (result) return true; 13468 if (node instanceof AST_Super) return result = true; 13469 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true; 13470 })); 13471 return result; 13472 }); 13473 13474 // contains_this() 13475 // returns false only if context bound by the specified scope (or scope 13476 // containing the specified expression) is not referenced by `this` 13477 (function(def) { 13478 // scope of arrow function cannot bind to any context 13479 def(AST_Arrow, return_false); 13480 def(AST_AsyncArrow, return_false); 13481 def(AST_Node, function() { 13482 var result = false; 13483 var self = this; 13484 self.walk(new TreeWalker(function(node) { 13485 if (result) return true; 13486 if (node instanceof AST_This) return result = true; 13487 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true; 13488 })); 13489 return result; 13490 }); 13491 })(function(node, func) { 13492 node.DEFMETHOD("contains_this", func); 13493 }); 13494 13495 function can_hoist_property(prop) { 13496 return prop instanceof AST_ObjectKeyVal 13497 && typeof prop.key == "string" 13498 && !(prop instanceof AST_ObjectMethod && prop.value.contains_super()); 13499 } 13500 13501 AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) { 13502 if (!compressor.option("properties")) return; 13503 if (key === "__proto__") return; 13504 var self = this; 13505 var expr = self.expression; 13506 if (!(expr instanceof AST_Object)) return; 13507 var props = expr.properties; 13508 for (var i = props.length; --i >= 0;) { 13509 var prop = props[i]; 13510 if (prop.key !== key) continue; 13511 if (!all(props, can_hoist_property)) return; 13512 if (!safe_to_flatten(prop.value, compressor)) return; 13513 var call, scope, values = []; 13514 for (var j = 0; j < props.length; j++) { 13515 var value = props[j].value; 13516 if (props[j] instanceof AST_ObjectMethod) { 13517 var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this()); 13518 if (arrow) { 13519 if (!scope) scope = compressor.find_parent(AST_Scope); 13520 var avoid = avoid_await_yield(compressor, scope); 13521 value.each_argname(function(argname) { 13522 if (avoid[argname.name]) arrow = false; 13523 }); 13524 } 13525 var ctor; 13526 if (arrow) { 13527 ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow; 13528 } else if (i != j 13529 || (call = compressor.parent()) instanceof AST_Call && call.expression === self) { 13530 ctor = value.CTOR; 13531 } else { 13532 return; 13533 } 13534 value = make_node(ctor, value); 13535 } 13536 values.push(value); 13537 } 13538 return make_node(AST_Sub, self, { 13539 expression: make_node(AST_Array, expr, { elements: values }), 13540 property: make_node(AST_Number, self, { value: i }), 13541 }); 13542 } 13543 }); 13544 13545 OPT(AST_Dot, function(self, compressor) { 13546 if (self.property == "arguments" || self.property == "caller") { 13547 AST_Node.warn("Function.prototype.{property} not supported [{start}]", self); 13548 } 13549 var parent = compressor.parent(); 13550 if (is_lhs(compressor.self(), parent)) return self; 13551 var terminated = trim_optional_chain(self, compressor); 13552 if (terminated) return terminated; 13553 if (compressor.option("sequences") 13554 && parent.TYPE != "Call" 13555 && !(parent instanceof AST_ForEnumeration && parent.init === self)) { 13556 var seq = lift_sequence_in_expression(self, compressor); 13557 if (seq !== self) return seq.optimize(compressor); 13558 } 13559 if (compressor.option("unsafe_proto") 13560 && self.expression instanceof AST_Dot 13561 && self.expression.property == "prototype") { 13562 var exp = self.expression.expression; 13563 if (is_undeclared_ref(exp)) switch (exp.name) { 13564 case "Array": 13565 self.expression = make_node(AST_Array, self.expression, { elements: [] }); 13566 break; 13567 case "Function": 13568 self.expression = make_node(AST_Function, self.expression, { 13569 argnames: [], 13570 body: [], 13571 }).init_vars(exp.scope); 13572 break; 13573 case "Number": 13574 self.expression = make_node(AST_Number, self.expression, { value: 0 }); 13575 break; 13576 case "Object": 13577 self.expression = make_node(AST_Object, self.expression, { properties: [] }); 13578 break; 13579 case "RegExp": 13580 self.expression = make_node(AST_RegExp, self.expression, { value: /t/ }); 13581 break; 13582 case "String": 13583 self.expression = make_node(AST_String, self.expression, { value: "" }); 13584 break; 13585 } 13586 } 13587 var sub = self.flatten_object(self.property, compressor); 13588 if (sub) return sub.optimize(compressor); 13589 return try_evaluate(compressor, self); 13590 }); 13591 13592 OPT(AST_DestructuredArray, function(self, compressor) { 13593 if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) { 13594 return make_node(AST_DestructuredArray, self, { 13595 elements: self.elements.concat(self.rest.elements), 13596 rest: self.rest.rest, 13597 }); 13598 } 13599 return self; 13600 }); 13601 13602 OPT(AST_DestructuredKeyVal, function(self, compressor) { 13603 if (compressor.option("objects")) { 13604 var key = self.key; 13605 if (key instanceof AST_Node) { 13606 key = key.evaluate(compressor); 13607 if (key !== self.key) self.key = "" + key; 13608 } 13609 } 13610 return self; 13611 }); 13612 13613 OPT(AST_Object, function(self, compressor) { 13614 if (!compressor.option("objects")) return self; 13615 var changed = false; 13616 var found = false; 13617 var generated = false; 13618 var keep_duplicate = compressor.has_directive("use strict"); 13619 var keys = []; 13620 var map = new Dictionary(); 13621 var values = []; 13622 self.properties.forEach(function(prop) { 13623 if (!(prop instanceof AST_Spread)) return process(prop); 13624 found = true; 13625 var exp = prop.expression; 13626 if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) { 13627 if (prop instanceof AST_ObjectGetter) return false; 13628 if (prop instanceof AST_Spread) return false; 13629 if (prop.key !== "__proto__") return true; 13630 if (prop instanceof AST_ObjectSetter) return true; 13631 return !prop.value.has_side_effects(compressor); 13632 })) { 13633 changed = true; 13634 exp.properties.forEach(function(prop) { 13635 var key = prop.key; 13636 var setter = prop instanceof AST_ObjectSetter; 13637 if (key === "__proto__") { 13638 if (!setter) return; 13639 key = make_node_from_constant(key, prop); 13640 } 13641 process(setter ? make_node(AST_ObjectKeyVal, prop, { 13642 key: key, 13643 value: make_node(AST_Undefined, prop).optimize(compressor), 13644 }) : prop); 13645 }); 13646 } else { 13647 generated = true; 13648 flush(); 13649 values.push(prop); 13650 } 13651 }); 13652 flush(); 13653 if (!changed) return self; 13654 if (found && generated && values.length == 1) { 13655 var value = values[0]; 13656 if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) { 13657 value.key = "" + value.key.value; 13658 } 13659 } 13660 return make_node(AST_Object, self, { properties: values }); 13661 13662 function flush() { 13663 keys.forEach(function(key) { 13664 var props = map.get(key); 13665 switch (props.length) { 13666 case 0: 13667 return; 13668 case 1: 13669 return values.push(props[0]); 13670 } 13671 changed = true; 13672 var tail = keep_duplicate && !generated && props.pop(); 13673 values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, { 13674 key: props[0].key, 13675 value: make_sequence(self, props.map(function(prop) { 13676 return prop.value; 13677 })), 13678 })); 13679 if (tail) values.push(tail); 13680 props.length = 0; 13681 }); 13682 keys = []; 13683 map = new Dictionary(); 13684 } 13685 13686 function process(prop) { 13687 var key = prop.key; 13688 if (key instanceof AST_Node) { 13689 found = true; 13690 key = key.evaluate(compressor); 13691 if (key === prop.key || key === "__proto__") { 13692 generated = true; 13693 } else { 13694 key = prop.key = "" + key; 13695 } 13696 } 13697 if (can_hoist_property(prop)) { 13698 if (prop.value.has_side_effects(compressor)) flush(); 13699 keys.push(key); 13700 map.add(key, prop); 13701 } else { 13702 flush(); 13703 values.push(prop); 13704 } 13705 if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) { 13706 generated = true; 13707 if (map.has(key)) prop = map.get(key)[0]; 13708 prop.key = make_node(AST_Number, prop, { value: +key }); 13709 } 13710 } 13711 }); 13712 13713 function flatten_var(name) { 13714 var redef = name.definition().redefined(); 13715 if (redef) { 13716 name = name.clone(); 13717 name.thedef = redef; 13718 } 13719 return name; 13720 } 13721 13722 function has_arg_refs(fn, node) { 13723 var found = false; 13724 node.walk(new TreeWalker(function(node) { 13725 if (found) return true; 13726 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) { 13727 return found = true; 13728 } 13729 })); 13730 return found; 13731 } 13732 13733 function insert_assign(def, assign) { 13734 var visited = []; 13735 def.references.forEach(function(ref) { 13736 var fixed = ref.fixed; 13737 if (!fixed || !push_uniq(visited, fixed)) return; 13738 if (fixed.assigns) { 13739 fixed.assigns.unshift(assign); 13740 } else { 13741 fixed.assigns = [ assign ]; 13742 } 13743 }); 13744 } 13745 13746 function init_ref(compressor, name) { 13747 var sym = make_node(AST_SymbolRef, name); 13748 var assign = make_node(AST_Assign, name, { 13749 operator: "=", 13750 left: sym, 13751 right: make_node(AST_Undefined, name).transform(compressor), 13752 }); 13753 var def = name.definition(); 13754 if (def.fixed) { 13755 sym.fixed = function() { 13756 return assign.right; 13757 }; 13758 sym.fixed.assigns = [ assign ]; 13759 insert_assign(def, assign); 13760 } 13761 def.assignments++; 13762 def.references.push(sym); 13763 return assign; 13764 } 13765 13766 (function(def) { 13767 def(AST_Node, noop); 13768 def(AST_Assign, noop); 13769 def(AST_Await, function(compressor, scope, no_return, in_loop) { 13770 if (!compressor.option("awaits")) return; 13771 var self = this; 13772 var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, true); 13773 if (!inlined) return; 13774 if (!no_return) scan_local_returns(inlined, function(node) { 13775 node.in_bool = false; 13776 var value = node.value; 13777 if (value instanceof AST_Await) return; 13778 node.value = make_node(AST_Await, self, { 13779 expression: value || make_node(AST_Undefined, node).transform(compressor), 13780 }); 13781 }); 13782 return aborts(inlined) ? inlined : make_node(AST_BlockStatement, self, { 13783 body: [ inlined, make_node(AST_SimpleStatement, self, { 13784 body: make_node(AST_Await, self, { expression: make_node(AST_Number, self, { value: 0 })}), 13785 }) ], 13786 }); 13787 }); 13788 def(AST_Binary, function(compressor, scope, no_return, in_loop, in_await) { 13789 if (no_return === undefined) return; 13790 var self = this; 13791 var op = self.operator; 13792 if (!lazy_op[op]) return; 13793 var inlined = self.right.try_inline(compressor, scope, no_return, in_loop, in_await); 13794 if (!inlined) return; 13795 return make_node(AST_If, self, { 13796 condition: make_condition(self.left), 13797 body: inlined, 13798 alternative: no_return ? null : make_node(AST_Return, self, { 13799 value: make_node(AST_Undefined, self).transform(compressor), 13800 }), 13801 }); 13802 13803 function make_condition(cond) { 13804 switch (op) { 13805 case "&&": 13806 return cond; 13807 case "||": 13808 return cond.negate(compressor); 13809 case "??": 13810 return make_node(AST_Binary, self, { 13811 operator: "==", 13812 left: make_node(AST_Null, self), 13813 right: cond, 13814 }); 13815 } 13816 } 13817 }); 13818 def(AST_BlockStatement, function(compressor, scope, no_return, in_loop) { 13819 if (no_return) return; 13820 if (!this.variables) return; 13821 var body = this.body; 13822 var last = body.length - 1; 13823 if (last < 0) return; 13824 var inlined = body[last].try_inline(compressor, this, no_return, in_loop); 13825 if (!inlined) return; 13826 body[last] = inlined; 13827 return this; 13828 }); 13829 def(AST_Call, function(compressor, scope, no_return, in_loop, in_await) { 13830 if (compressor.option("inline") < 4) return; 13831 var call = this; 13832 if (call.is_expr_pure(compressor)) return; 13833 var fn = call.expression; 13834 if (!(fn instanceof AST_LambdaExpression)) return; 13835 if (fn.name) return; 13836 if (fn.uses_arguments) return; 13837 if (fn.pinned()) return; 13838 if (is_generator(fn)) return; 13839 var arrow = is_arrow(fn); 13840 if (arrow && fn.value) return; 13841 if (fn.body[0] instanceof AST_Directive) return; 13842 if (fn.contains_this()) return; 13843 if (!scope) scope = find_scope(compressor); 13844 var defined = new Dictionary(); 13845 defined.set("NaN", true); 13846 while (!(scope instanceof AST_Scope)) { 13847 scope.variables.each(function(def) { 13848 defined.set(def.name, true); 13849 }); 13850 scope = scope.parent_scope; 13851 } 13852 if (!member(scope, compressor.stack)) return; 13853 if (scope.pinned() && fn.variables.size() > (arrow ? 0 : 1)) return; 13854 if (scope instanceof AST_Toplevel) { 13855 if (fn.variables.size() > (arrow ? 0 : 1)) { 13856 if (!compressor.toplevel.vars) return; 13857 if (fn.functions.size() > 0 && !compressor.toplevel.funcs) return; 13858 } 13859 defined.set("arguments", true); 13860 } 13861 var async = !in_await && is_async(fn); 13862 if (async) { 13863 if (!compressor.option("awaits")) return; 13864 if (!is_async(scope)) return; 13865 if (call.may_throw(compressor)) return; 13866 } 13867 var names = scope.var_names(); 13868 if (in_loop) in_loop = []; 13869 if (!fn.variables.all(function(def, name) { 13870 if (in_loop) in_loop.push(def); 13871 if (!defined.has(name) && !names.has(name)) return true; 13872 return !arrow && name == "arguments" && def.orig.length == 1; 13873 })) return; 13874 if (in_loop && in_loop.length > 0 && is_reachable(fn, in_loop)) return; 13875 var simple_argnames = true; 13876 if (!all(fn.argnames, function(argname) { 13877 var abort = false; 13878 var tw = new TreeWalker(function(node) { 13879 if (abort) return true; 13880 if (node instanceof AST_DefaultValue) { 13881 if (has_arg_refs(fn, node.value)) return abort = true; 13882 node.name.walk(tw); 13883 return true; 13884 } 13885 if (node instanceof AST_DestructuredKeyVal) { 13886 if (node.key instanceof AST_Node && has_arg_refs(fn, node.key)) return abort = true; 13887 node.value.walk(tw); 13888 return true; 13889 } 13890 if (node instanceof AST_SymbolFunarg && !all(node.definition().orig, function(sym) { 13891 return !(sym instanceof AST_SymbolDefun); 13892 })) return abort = true; 13893 }); 13894 argname.walk(tw); 13895 if (abort) return false; 13896 if (!(argname instanceof AST_SymbolFunarg)) simple_argnames = false; 13897 return true; 13898 })) return; 13899 if (fn.rest) { 13900 if (has_arg_refs(fn, fn.rest)) return; 13901 simple_argnames = false; 13902 } 13903 var verify_body; 13904 if (no_return) { 13905 verify_body = function(stat) { 13906 var abort = false; 13907 stat.walk(new TreeWalker(function(node) { 13908 if (abort) return true; 13909 if (async && (node instanceof AST_Await || node instanceof AST_ForAwaitOf) 13910 || node instanceof AST_Return) { 13911 return abort = true; 13912 } 13913 if (node instanceof AST_Scope) return true; 13914 })); 13915 return !abort; 13916 }; 13917 } else if (in_await || is_async(fn) || in_async_generator(scope)) { 13918 verify_body = function(stat) { 13919 var abort = false; 13920 var find_return = new TreeWalker(function(node) { 13921 if (abort) return true; 13922 if (node instanceof AST_Return) return abort = true; 13923 if (node instanceof AST_Scope) return true; 13924 }); 13925 stat.walk(new TreeWalker(function(node) { 13926 if (abort) return true; 13927 if (node instanceof AST_Try) { 13928 if (node.bfinally && all(node.body, function(stat) { 13929 stat.walk(find_return); 13930 return !abort; 13931 }) && node.bcatch) node.bcatch.walk(find_return); 13932 return true; 13933 } 13934 if (node instanceof AST_Scope) return true; 13935 })); 13936 return !abort; 13937 }; 13938 } 13939 if (verify_body && !all(fn.body, verify_body)) return; 13940 if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return; 13941 fn.functions.each(function(def, name) { 13942 scope.functions.set(name, def); 13943 }); 13944 var body = []; 13945 fn.variables.each(function(def, name) { 13946 if (!arrow && name == "arguments" && def.orig.length == 1) return; 13947 names.set(name, true); 13948 scope.enclosed.push(def); 13949 scope.variables.set(name, def); 13950 def.single_use = false; 13951 if (!in_loop) return; 13952 if (def.references.length == def.replaced) return; 13953 if (def.orig.length == def.eliminated) return; 13954 if (def.orig.length == 1 && fn.functions.has(name)) return; 13955 if (!all(def.orig, function(sym) { 13956 if (sym instanceof AST_SymbolConst) return false; 13957 if (sym instanceof AST_SymbolFunarg) return !sym.unused && def.scope.resolve() !== fn; 13958 if (sym instanceof AST_SymbolLet) return false; 13959 return true; 13960 })) return; 13961 var sym = def.orig[0]; 13962 if (sym instanceof AST_SymbolCatch) return; 13963 body.push(make_node(AST_SimpleStatement, sym, { body: init_ref(compressor, flatten_var(sym)) })); 13964 }); 13965 var defs = Object.create(null), syms = new Dictionary(); 13966 if (simple_argnames && all(call.args, function(arg) { 13967 return !(arg instanceof AST_Spread); 13968 })) { 13969 var values = call.args.slice(); 13970 fn.argnames.forEach(function(argname) { 13971 var value = values.shift(); 13972 if (argname.unused) { 13973 if (value) body.push(make_node(AST_SimpleStatement, call, { body: value })); 13974 return; 13975 } 13976 var defn = make_node(AST_VarDef, call, { 13977 name: argname.convert_symbol(AST_SymbolVar, process), 13978 value: value || make_node(AST_Undefined, call).transform(compressor), 13979 }); 13980 if (argname instanceof AST_SymbolFunarg) insert_assign(argname.definition(), defn); 13981 body.push(make_node(AST_Var, call, { definitions: [ defn ] })); 13982 }); 13983 if (values.length) body.push(make_node(AST_SimpleStatement, call, { 13984 body: make_sequence(call, values), 13985 })); 13986 } else { 13987 body.push(make_node(AST_Var, call, { 13988 definitions: [ make_node(AST_VarDef, call, { 13989 name: make_node(AST_DestructuredArray, call, { 13990 elements: fn.argnames.map(function(argname) { 13991 if (argname.unused) return make_node(AST_Hole, argname); 13992 return argname.convert_symbol(AST_SymbolVar, process); 13993 }), 13994 rest: fn.rest && fn.rest.convert_symbol(AST_SymbolVar, process), 13995 }), 13996 value: make_node(AST_Array, call, { elements: call.args.slice() }), 13997 }) ], 13998 })); 13999 } 14000 syms.each(function(orig, id) { 14001 var def = defs[id]; 14002 [].unshift.apply(def.orig, orig); 14003 def.eliminated += orig.length; 14004 }); 14005 [].push.apply(body, in_loop ? fn.body.filter(function(stat) { 14006 if (!(stat instanceof AST_LambdaDefinition)) return true; 14007 var name = make_node(AST_SymbolVar, flatten_var(stat.name)); 14008 var def = name.definition(); 14009 def.fixed = false; 14010 def.orig.push(name); 14011 def.eliminated++; 14012 body.push(make_node(AST_Var, stat, { 14013 definitions: [ make_node(AST_VarDef, stat, { 14014 name: name, 14015 value: to_func_expr(stat, true), 14016 }) ], 14017 })); 14018 return false; 14019 }) : fn.body); 14020 var inlined = make_node(AST_BlockStatement, call, { body: body }); 14021 if (!no_return) { 14022 if (async) scan_local_returns(inlined, function(node) { 14023 var value = node.value; 14024 if (is_undefined(value)) return; 14025 node.value = make_node(AST_Await, call, { expression: value }); 14026 }); 14027 body.push(make_node(AST_Return, call, { 14028 value: in_async_generator(scope) ? make_node(AST_Undefined, call).transform(compressor) : null, 14029 })); 14030 } 14031 return inlined; 14032 14033 function process(sym, argname) { 14034 var def = argname.definition(); 14035 defs[def.id] = def; 14036 syms.add(def.id, sym); 14037 } 14038 }); 14039 def(AST_Conditional, function(compressor, scope, no_return, in_loop, in_await) { 14040 var self = this; 14041 var body = self.consequent.try_inline(compressor, scope, no_return, in_loop, in_await); 14042 var alt = self.alternative.try_inline(compressor, scope, no_return, in_loop, in_await); 14043 if (!body && !alt) return; 14044 return make_node(AST_If, self, { 14045 condition: self.condition, 14046 body: body || make_body(self.consequent), 14047 alternative: alt || make_body(self.alternative), 14048 }); 14049 14050 function make_body(value) { 14051 if (no_return) return make_node(AST_SimpleStatement, value, { body: value }); 14052 return make_node(AST_Return, value, { value: value }); 14053 } 14054 }); 14055 def(AST_For, function(compressor, scope, no_return, in_loop) { 14056 var body = this.body.try_inline(compressor, scope, true, true); 14057 if (body) this.body = body; 14058 var inlined = this.init; 14059 if (inlined) { 14060 inlined = inlined.try_inline(compressor, scope, true, in_loop); 14061 if (inlined) { 14062 this.init = null; 14063 if (inlined instanceof AST_BlockStatement) { 14064 inlined.body.push(this); 14065 return inlined; 14066 } 14067 return make_node(AST_BlockStatement, inlined, { body: [ inlined, this ] }); 14068 } 14069 } 14070 return body && this; 14071 }); 14072 def(AST_ForEnumeration, function(compressor, scope, no_return, in_loop) { 14073 var body = this.body.try_inline(compressor, scope, true, true); 14074 if (body) this.body = body; 14075 var obj = this.object; 14076 if (obj instanceof AST_Sequence) { 14077 var inlined = inline_sequence(compressor, scope, true, in_loop, false, obj, 1); 14078 if (inlined) { 14079 this.object = obj.tail_node(); 14080 inlined.body.push(this); 14081 return inlined; 14082 } 14083 } 14084 return body && this; 14085 }); 14086 def(AST_If, function(compressor, scope, no_return, in_loop) { 14087 var body = this.body.try_inline(compressor, scope, no_return, in_loop); 14088 if (body) this.body = body; 14089 var alt = this.alternative; 14090 if (alt) { 14091 alt = alt.try_inline(compressor, scope, no_return, in_loop); 14092 if (alt) this.alternative = alt; 14093 } 14094 var cond = this.condition; 14095 if (cond instanceof AST_Sequence) { 14096 var inlined = inline_sequence(compressor, scope, true, in_loop, false, cond, 1); 14097 if (inlined) { 14098 this.condition = cond.tail_node(); 14099 inlined.body.push(this); 14100 return inlined; 14101 } 14102 } 14103 return (body || alt) && this; 14104 }); 14105 def(AST_IterationStatement, function(compressor, scope, no_return, in_loop) { 14106 var body = this.body.try_inline(compressor, scope, true, true); 14107 if (!body) return; 14108 this.body = body; 14109 return this; 14110 }); 14111 def(AST_LabeledStatement, function(compressor, scope, no_return, in_loop) { 14112 var body = this.body.try_inline(compressor, scope, no_return, in_loop); 14113 if (!body) return; 14114 if (this.body instanceof AST_IterationStatement && body instanceof AST_BlockStatement) { 14115 var loop = body.body.pop(); 14116 this.body = loop; 14117 body.body.push(this); 14118 return body; 14119 } 14120 this.body = body; 14121 return this; 14122 }); 14123 def(AST_New, noop); 14124 def(AST_Return, function(compressor, scope, no_return, in_loop) { 14125 var value = this.value; 14126 return value && value.try_inline(compressor, scope, undefined, in_loop === "try"); 14127 }); 14128 function inline_sequence(compressor, scope, no_return, in_loop, in_await, node, skip) { 14129 var body = [], exprs = node.expressions, no_ret = no_return; 14130 for (var i = exprs.length - (skip || 0), j = i; --i >= 0; no_ret = true, in_await = false) { 14131 var inlined = exprs[i].try_inline(compressor, scope, no_ret, in_loop, in_await); 14132 if (!inlined) continue; 14133 flush(); 14134 body.push(inlined); 14135 } 14136 if (body.length == 0) return; 14137 flush(); 14138 if (!no_return && body[0] instanceof AST_SimpleStatement) { 14139 body[0] = make_node(AST_Return, node, { value: body[0].body }); 14140 } 14141 return make_node(AST_BlockStatement, node, { body: body.reverse() }); 14142 14143 function flush() { 14144 if (j > i + 1) body.push(make_node(AST_SimpleStatement, node, { 14145 body: make_sequence(node, exprs.slice(i + 1, j)), 14146 })); 14147 j = i; 14148 } 14149 } 14150 def(AST_Sequence, function(compressor, scope, no_return, in_loop, in_await) { 14151 return inline_sequence(compressor, scope, no_return, in_loop, in_await, this); 14152 }); 14153 def(AST_SimpleStatement, function(compressor, scope, no_return, in_loop) { 14154 var body = this.body; 14155 while (body instanceof AST_UnaryPrefix) { 14156 var op = body.operator; 14157 if (unary_side_effects[op]) break; 14158 if (op == "void") break; 14159 body = body.expression; 14160 } 14161 if (!no_return && !is_undefined(body)) body = make_node(AST_UnaryPrefix, this, { 14162 operator: "void", 14163 expression: body, 14164 }); 14165 return body.try_inline(compressor, scope, no_return || false, in_loop); 14166 }); 14167 def(AST_UnaryPrefix, function(compressor, scope, no_return, in_loop, in_await) { 14168 var self = this; 14169 var op = self.operator; 14170 if (unary_side_effects[op]) return; 14171 if (!no_return && op == "void") no_return = false; 14172 var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, in_await); 14173 if (!inlined) return; 14174 if (!no_return) scan_local_returns(inlined, function(node) { 14175 node.in_bool = false; 14176 var value = node.value; 14177 if (op == "void" && is_undefined(value)) return; 14178 node.value = make_node(AST_UnaryPrefix, self, { 14179 operator: op, 14180 expression: value || make_node(AST_Undefined, node).transform(compressor), 14181 }); 14182 }); 14183 return inlined; 14184 }); 14185 def(AST_With, function(compressor, scope, no_return, in_loop) { 14186 var body = this.body.try_inline(compressor, scope, no_return, in_loop); 14187 if (body) this.body = body; 14188 var exp = this.expression; 14189 if (exp instanceof AST_Sequence) { 14190 var inlined = inline_sequence(compressor, scope, true, in_loop, false, exp, 1); 14191 if (inlined) { 14192 this.expression = exp.tail_node(); 14193 inlined.body.push(this); 14194 return inlined; 14195 } 14196 } 14197 return body && this; 14198 }); 14199 def(AST_Yield, function(compressor, scope, no_return, in_loop) { 14200 if (!compressor.option("yields")) return; 14201 if (!this.nested) return; 14202 var call = this.expression; 14203 if (call.TYPE != "Call") return; 14204 var fn = call.expression; 14205 switch (fn.CTOR) { 14206 case AST_AsyncGeneratorFunction: 14207 fn = make_node(AST_AsyncFunction, fn); 14208 break; 14209 case AST_GeneratorFunction: 14210 fn = make_node(AST_Function, fn); 14211 break; 14212 default: 14213 return; 14214 } 14215 call = call.clone(); 14216 call.expression = fn; 14217 return call.try_inline(compressor, scope, no_return, in_loop); 14218 }); 14219 })(function(node, func) { 14220 node.DEFMETHOD("try_inline", func); 14221 }); 14222 14223 OPT(AST_Return, function(self, compressor) { 14224 var value = self.value; 14225 if (value && compressor.option("side_effects") 14226 && is_undefined(value, compressor) 14227 && !in_async_generator(compressor.find_parent(AST_Scope))) { 14228 self.value = null; 14229 } 14230 return self; 14231 }); 14232})(function(node, optimizer) { 14233 node.DEFMETHOD("optimize", function(compressor) { 14234 var self = this; 14235 if (self._optimized) return self; 14236 if (compressor.has_directive("use asm")) return self; 14237 var opt = optimizer(self, compressor); 14238 opt._optimized = true; 14239 return opt; 14240 }); 14241}); 14242