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 is_some_comments(comment) { 47 // multiline comment 48 return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value); 49} 50 51function OutputStream(options) { 52 options = defaults(options, { 53 annotations : false, 54 ascii_only : false, 55 beautify : false, 56 braces : false, 57 comments : false, 58 extendscript : false, 59 galio : false, 60 ie : false, 61 indent_level : 4, 62 indent_start : 0, 63 inline_script : true, 64 keep_quoted_props: false, 65 max_line_len : false, 66 preamble : null, 67 preserve_line : false, 68 quote_keys : false, 69 quote_style : 0, 70 semicolons : true, 71 shebang : true, 72 source_map : null, 73 v8 : false, 74 webkit : false, 75 width : 80, 76 wrap_iife : false, 77 }, true); 78 79 // Convert comment option to RegExp if necessary and set up comments filter 80 var comment_filter = return_false; // Default case, throw all comments away 81 if (options.comments) { 82 var comments = options.comments; 83 if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) { 84 var regex_pos = options.comments.lastIndexOf("/"); 85 comments = new RegExp( 86 options.comments.substr(1, regex_pos - 1), 87 options.comments.substr(regex_pos + 1) 88 ); 89 } 90 if (comments instanceof RegExp) { 91 comment_filter = function(comment) { 92 return comment.type != "comment5" && comments.test(comment.value); 93 }; 94 } else if (typeof comments === "function") { 95 comment_filter = function(comment) { 96 return comment.type != "comment5" && comments(this, comment); 97 }; 98 } else if (comments === "some") { 99 comment_filter = is_some_comments; 100 } else { // NOTE includes "all" option 101 comment_filter = return_true; 102 } 103 } 104 105 function make_indent(value) { 106 if (typeof value == "number") return new Array(value + 1).join(" "); 107 if (!value) return ""; 108 if (!/^\s*$/.test(value)) throw new Error("unsupported indentation: " + JSON.stringify("" + value)); 109 return value; 110 } 111 112 var current_col = 0; 113 var current_line = 1; 114 var current_indent = make_indent(options.indent_start); 115 var full_indent = make_indent(options.indent_level); 116 var half_indent = full_indent.length + 1 >> 1; 117 var last; 118 var line_end = 0; 119 var line_fixed = true; 120 var mappings = options.source_map && []; 121 var mapping_name; 122 var mapping_token; 123 var might_need_space; 124 var might_need_semicolon; 125 var need_newline_indented = false; 126 var need_space = false; 127 var output; 128 var stack; 129 var stored = ""; 130 131 function reset() { 132 last = ""; 133 might_need_space = false; 134 might_need_semicolon = false; 135 stack = []; 136 var str = output; 137 output = ""; 138 return str; 139 } 140 141 reset(); 142 var to_utf8 = options.ascii_only ? function(str, identifier) { 143 if (identifier) str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) { 144 return "\\u{" + (ch.charCodeAt(0) - 0xd7c0 << 10 | ch.charCodeAt(1) - 0xdc00).toString(16) + "}"; 145 }); 146 return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) { 147 var code = ch.charCodeAt(0).toString(16); 148 if (code.length <= 2 && !identifier) { 149 while (code.length < 2) code = "0" + code; 150 return "\\x" + code; 151 } else { 152 while (code.length < 4) code = "0" + code; 153 return "\\u" + code; 154 } 155 }); 156 } : function(str) { 157 var s = ""; 158 for (var i = 0, j = 0; i < str.length; i++) { 159 var code = str.charCodeAt(i); 160 if (is_surrogate_pair_head(code)) { 161 if (is_surrogate_pair_tail(str.charCodeAt(i + 1))) { 162 i++; 163 continue; 164 } 165 } else if (!is_surrogate_pair_tail(code)) { 166 continue; 167 } 168 s += str.slice(j, i) + "\\u" + code.toString(16); 169 j = i + 1; 170 } 171 return j == 0 ? str : s + str.slice(j); 172 }; 173 174 function quote_single(str) { 175 return "'" + str.replace(/\x27/g, "\\'") + "'"; 176 } 177 178 function quote_double(str) { 179 return '"' + str.replace(/\x22/g, '\\"') + '"'; 180 } 181 182 var quote_string = [ 183 null, 184 quote_single, 185 quote_double, 186 function(str, quote) { 187 return quote == "'" ? quote_single(str) : quote_double(str); 188 }, 189 ][options.quote_style] || function(str, quote, dq, sq) { 190 return dq > sq ? quote_single(str) : quote_double(str); 191 }; 192 193 function make_string(str, quote) { 194 var dq = 0, sq = 0; 195 str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) { 196 switch (s) { 197 case '"': ++dq; return '"'; 198 case "'": ++sq; return "'"; 199 case "\\": return "\\\\"; 200 case "\n": return "\\n"; 201 case "\r": return "\\r"; 202 case "\t": return "\\t"; 203 case "\b": return "\\b"; 204 case "\f": return "\\f"; 205 case "\x0B": return options.ie ? "\\x0B" : "\\v"; 206 case "\u2028": return "\\u2028"; 207 case "\u2029": return "\\u2029"; 208 case "\ufeff": return "\\ufeff"; 209 case "\0": 210 return /[0-9]/.test(str.charAt(i+1)) ? "\\x00" : "\\0"; 211 } 212 return s; 213 }); 214 return quote_string(to_utf8(str), quote, dq, sq); 215 } 216 217 /* -----[ beautification/minification ]----- */ 218 219 var adjust_mappings = mappings ? function(line, col) { 220 mappings.forEach(function(mapping) { 221 mapping.line += line; 222 mapping.col += col; 223 }); 224 } : noop; 225 226 var flush_mappings = mappings ? function() { 227 mappings.forEach(function(mapping) { 228 options.source_map.add( 229 mapping.token.file, 230 mapping.line, mapping.col, 231 mapping.token.line, mapping.token.col, 232 !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name 233 ); 234 }); 235 mappings = []; 236 } : noop; 237 238 function insert_newlines(count) { 239 stored += output.slice(0, line_end); 240 output = output.slice(line_end); 241 var new_col = output.length; 242 adjust_mappings(count, new_col - current_col); 243 current_line += count; 244 current_col = new_col; 245 while (count--) stored += "\n"; 246 } 247 248 var fix_line = options.max_line_len ? function(flush) { 249 if (line_fixed) { 250 if (current_col > options.max_line_len) { 251 AST_Node.warn("Output exceeds {max_line_len} characters", options); 252 } 253 return; 254 } 255 if (current_col > options.max_line_len) { 256 insert_newlines(1); 257 line_fixed = true; 258 } 259 if (line_fixed || flush) flush_mappings(); 260 } : noop; 261 262 var require_semicolon = makePredicate("( [ + * / - , ."); 263 264 function require_space(prev, ch, str) { 265 return is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\") 266 || (ch == "/" && ch == prev) 267 || ((ch == "+" || ch == "-") && ch == last) 268 || last == "--" && ch == ">" 269 || last == "!" && str == "--" 270 || prev == "/" && (str == "in" || str == "instanceof"); 271 } 272 273 var print = options.beautify 274 || options.comments 275 || options.max_line_len 276 || options.preserve_line 277 || options.shebang 278 || !options.semicolons 279 || options.source_map 280 || options.width ? function(str) { 281 var ch = str.charAt(0); 282 if (need_newline_indented && ch) { 283 need_newline_indented = false; 284 if (ch != "\n") { 285 print("\n"); 286 indent(); 287 } 288 } 289 if (need_space && ch) { 290 need_space = false; 291 if (!/[\s;})]/.test(ch)) { 292 space(); 293 } 294 } 295 var prev = last.slice(-1); 296 if (might_need_semicolon) { 297 might_need_semicolon = false; 298 if (prev == ":" && ch == "}" || prev != ";" && (!ch || ";}".indexOf(ch) < 0)) { 299 var need_semicolon = require_semicolon[ch]; 300 if (need_semicolon || options.semicolons) { 301 output += ";"; 302 current_col++; 303 if (!line_fixed) { 304 fix_line(); 305 if (line_fixed && !need_semicolon && output == ";") { 306 output = ""; 307 current_col = 0; 308 } 309 } 310 if (line_end == output.length - 1) line_end++; 311 } else { 312 fix_line(); 313 output += "\n"; 314 current_line++; 315 current_col = 0; 316 // reset the semicolon flag, since we didn't print one 317 // now and might still have to later 318 if (/^\s+$/.test(str)) might_need_semicolon = true; 319 } 320 if (!options.beautify) might_need_space = false; 321 } 322 } 323 324 if (might_need_space) { 325 if (require_space(prev, ch, str)) { 326 output += " "; 327 current_col++; 328 } 329 if (prev != "<" || str != "!") might_need_space = false; 330 } 331 332 if (mapping_token) { 333 mappings.push({ 334 token: mapping_token, 335 name: mapping_name, 336 line: current_line, 337 col: current_col, 338 }); 339 mapping_token = false; 340 if (line_fixed) flush_mappings(); 341 } 342 343 output += str; 344 var a = str.split(/\r?\n/), n = a.length - 1; 345 current_line += n; 346 current_col += a[0].length; 347 if (n > 0) { 348 fix_line(); 349 current_col = a[n].length; 350 } 351 last = str; 352 } : function(str) { 353 var ch = str.charAt(0); 354 var prev = last.slice(-1); 355 if (might_need_semicolon) { 356 might_need_semicolon = false; 357 if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") { 358 output += ";"; 359 might_need_space = false; 360 } 361 } 362 if (might_need_space) { 363 if (require_space(prev, ch, str)) output += " "; 364 if (prev != "<" || str != "!") might_need_space = false; 365 } 366 output += str; 367 last = str; 368 }; 369 370 var space = options.beautify ? function() { 371 print(" "); 372 } : function() { 373 might_need_space = true; 374 }; 375 376 var indent = options.beautify ? function(half) { 377 if (need_newline_indented) print("\n"); 378 print(half ? current_indent.slice(0, -half_indent) : current_indent); 379 } : noop; 380 381 var with_indent = options.beautify ? function(cont) { 382 var save_indentation = current_indent; 383 current_indent += full_indent; 384 cont(); 385 current_indent = save_indentation; 386 } : function(cont) { cont() }; 387 388 var may_add_newline = options.max_line_len || options.preserve_line ? function() { 389 fix_line(); 390 line_end = output.length; 391 line_fixed = false; 392 } : noop; 393 394 var newline = options.beautify ? function() { 395 print("\n"); 396 line_end = output.length; 397 } : may_add_newline; 398 399 var semicolon = options.beautify ? function() { 400 print(";"); 401 } : function() { 402 might_need_semicolon = true; 403 }; 404 405 function force_semicolon() { 406 if (might_need_semicolon) print(";"); 407 print(";"); 408 } 409 410 function with_block(cont, end) { 411 print("{"); 412 newline(); 413 with_indent(cont); 414 add_mapping(end); 415 indent(); 416 print("}"); 417 } 418 419 function with_parens(cont) { 420 print("("); 421 may_add_newline(); 422 cont(); 423 may_add_newline(); 424 print(")"); 425 } 426 427 function with_square(cont) { 428 print("["); 429 may_add_newline(); 430 cont(); 431 may_add_newline(); 432 print("]"); 433 } 434 435 function comma() { 436 may_add_newline(); 437 print(","); 438 may_add_newline(); 439 space(); 440 } 441 442 function colon() { 443 print(":"); 444 space(); 445 } 446 447 var add_mapping = mappings ? function(token, name) { 448 mapping_token = token; 449 mapping_name = name; 450 } : noop; 451 452 function get() { 453 if (!line_fixed) fix_line(true); 454 return stored + output; 455 } 456 457 function has_nlb() { 458 return /(^|\n) *$/.test(output); 459 } 460 461 function pad_comment(token, force) { 462 if (need_newline_indented) return; 463 if (token.nlb && (force || !has_nlb())) { 464 need_newline_indented = true; 465 } else if (force) { 466 need_space = true; 467 } 468 } 469 470 function print_comment(comment) { 471 var value = comment.value.replace(/[@#]__PURE__/g, " "); 472 if (/^\s*$/.test(value) && !/^\s*$/.test(comment.value)) return false; 473 if (/comment[134]/.test(comment.type)) { 474 print("//" + value); 475 need_newline_indented = true; 476 } else if (comment.type == "comment2") { 477 print("/*" + value + "*/"); 478 } 479 return true; 480 } 481 482 function should_merge_comments(node, parent) { 483 if (parent instanceof AST_Binary) return parent.left === node; 484 if (parent.TYPE == "Call") return parent.expression === node; 485 if (parent instanceof AST_Conditional) return parent.condition === node; 486 if (parent instanceof AST_Dot) return parent.expression === node; 487 if (parent instanceof AST_Exit) return true; 488 if (parent instanceof AST_Sequence) return parent.expressions[0] === node; 489 if (parent instanceof AST_Sub) return parent.expression === node; 490 if (parent instanceof AST_UnaryPostfix) return true; 491 if (parent instanceof AST_Yield) return true; 492 } 493 494 function prepend_comments(node) { 495 var self = this; 496 var scan; 497 if (node instanceof AST_Exit) { 498 scan = node.value; 499 } else if (node instanceof AST_Yield) { 500 scan = node.expression; 501 } 502 var comments = dump(node); 503 if (!comments) comments = []; 504 505 if (scan) { 506 var tw = new TreeWalker(function(node) { 507 if (!should_merge_comments(node, tw.parent())) return true; 508 var before = dump(node); 509 if (before) comments = comments.concat(before); 510 }); 511 tw.push(node); 512 scan.walk(tw); 513 } 514 515 if (current_line == 1 && current_col == 0) { 516 if (comments.length > 0 && options.shebang && comments[0].type == "comment5") { 517 print("#!" + comments.shift().value + "\n"); 518 indent(); 519 } 520 var preamble = options.preamble; 521 if (preamble) print(preamble.replace(/\r\n?|\u2028|\u2029|(^|\S)\s*$/g, "$1\n")); 522 } 523 524 comments = comments.filter(comment_filter, node); 525 var printed = false; 526 comments.forEach(function(comment, index) { 527 pad_comment(comment, index); 528 if (print_comment(comment)) printed = true; 529 }); 530 if (printed) pad_comment(node.start, true); 531 532 function dump(node) { 533 var token = node.start; 534 if (!token) { 535 if (!scan) return; 536 node.start = token = new AST_Token(); 537 } 538 var comments = token.comments_before; 539 if (!comments) { 540 if (!scan) return; 541 token.comments_before = comments = []; 542 } 543 if (comments._dumped === self) return; 544 comments._dumped = self; 545 return comments; 546 } 547 } 548 549 function append_comments(node, tail) { 550 var self = this; 551 var token = node.end; 552 if (!token) return; 553 var comments = token[tail ? "comments_before" : "comments_after"]; 554 if (!comments || comments._dumped === self) return; 555 if (!(node instanceof AST_Statement || all(comments, function(c) { 556 return !/comment[134]/.test(c.type); 557 }))) return; 558 comments._dumped = self; 559 comments.filter(comment_filter, node).forEach(function(comment, index) { 560 pad_comment(comment, index || !tail); 561 print_comment(comment); 562 }); 563 } 564 565 return { 566 get : get, 567 reset : reset, 568 indent : indent, 569 should_break : options.beautify && options.width ? function() { 570 return current_col >= options.width; 571 } : return_false, 572 has_parens : function() { return last.slice(-1) == "(" }, 573 newline : newline, 574 print : print, 575 space : space, 576 comma : comma, 577 colon : colon, 578 last : function() { return last }, 579 semicolon : semicolon, 580 force_semicolon : force_semicolon, 581 to_utf8 : to_utf8, 582 print_name : function(name) { print(to_utf8(name.toString(), true)) }, 583 print_string : options.inline_script ? function(str, quote) { 584 str = make_string(str, quote).replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2"); 585 print(str.replace(/\x3c!--/g, "\\x3c!--").replace(/--\x3e/g, "--\\x3e")); 586 } : function(str, quote) { 587 print(make_string(str, quote)); 588 }, 589 with_indent : with_indent, 590 with_block : with_block, 591 with_parens : with_parens, 592 with_square : with_square, 593 add_mapping : add_mapping, 594 option : function(opt) { return options[opt] }, 595 prepend_comments: options.comments || options.shebang ? prepend_comments : noop, 596 append_comments : options.comments ? append_comments : noop, 597 push_node : function(node) { stack.push(node) }, 598 pop_node : options.preserve_line ? function() { 599 var node = stack.pop(); 600 if (node.start && node.start.line > current_line) { 601 insert_newlines(node.start.line - current_line); 602 } 603 } : function() { 604 stack.pop(); 605 }, 606 parent : function(n) { 607 return stack[stack.length - 2 - (n || 0)]; 608 }, 609 }; 610} 611 612/* -----[ code generators ]----- */ 613 614(function() { 615 616 /* -----[ utils ]----- */ 617 618 function DEFPRINT(nodetype, generator) { 619 nodetype.DEFMETHOD("_codegen", generator); 620 } 621 622 var use_asm = false; 623 624 AST_Node.DEFMETHOD("print", function(stream, force_parens) { 625 var self = this; 626 stream.push_node(self); 627 if (force_parens || self.needs_parens(stream)) { 628 stream.with_parens(doit); 629 } else { 630 doit(); 631 } 632 stream.pop_node(); 633 634 function doit() { 635 stream.prepend_comments(self); 636 self.add_source_map(stream); 637 self._codegen(stream); 638 stream.append_comments(self); 639 } 640 }); 641 var readonly = OutputStream({ 642 inline_script: false, 643 shebang: false, 644 width: false, 645 }); 646 AST_Node.DEFMETHOD("print_to_string", function(options) { 647 if (options) { 648 var stream = OutputStream(options); 649 this.print(stream); 650 return stream.get(); 651 } 652 this.print(readonly); 653 return readonly.reset(); 654 }); 655 656 /* -----[ PARENTHESES ]----- */ 657 658 function PARENS(nodetype, func) { 659 nodetype.DEFMETHOD("needs_parens", func); 660 } 661 662 PARENS(AST_Node, return_false); 663 664 // a function expression needs parens around it when it's provably 665 // the first token to appear in a statement. 666 function needs_parens_function(output) { 667 var p = output.parent(); 668 if (!output.has_parens() && first_in_statement(output, false, true)) { 669 // export default function() {} 670 // export default (function foo() {}); 671 // export default (function() {})(foo); 672 // export default (function() {})`foo`; 673 // export default (function() {}) ? foo : bar; 674 return this.name || !(p instanceof AST_ExportDefault); 675 } 676 if (output.option("webkit") && p instanceof AST_PropAccess && p.expression === this) return true; 677 if (output.option("wrap_iife") && p instanceof AST_Call && p.expression === this) return true; 678 } 679 PARENS(AST_AsyncFunction, needs_parens_function); 680 PARENS(AST_AsyncGeneratorFunction, needs_parens_function); 681 PARENS(AST_ClassExpression, needs_parens_function); 682 PARENS(AST_Function, needs_parens_function); 683 PARENS(AST_GeneratorFunction, needs_parens_function); 684 685 // same goes for an object literal, because otherwise it would be 686 // interpreted as a block of code. 687 function needs_parens_obj(output) { 688 return !output.has_parens() && first_in_statement(output, true); 689 } 690 PARENS(AST_Object, needs_parens_obj); 691 692 function needs_parens_unary(output) { 693 var p = output.parent(); 694 // (-x) ** y 695 if (p instanceof AST_Binary) return p.operator == "**" && p.left === this; 696 // (await x)(y) 697 // new (await x) 698 if (p instanceof AST_Call) return p.expression === this; 699 // class extends (x++) {} 700 // class x extends (typeof y) {} 701 if (p instanceof AST_Class) return true; 702 // (x++)[y] 703 // (typeof x).y 704 // https://github.com/mishoo/UglifyJS/issues/115 705 if (p instanceof AST_PropAccess) return p.expression === this; 706 // (~x)`foo` 707 if (p instanceof AST_Template) return p.tag === this; 708 } 709 PARENS(AST_Await, needs_parens_unary); 710 PARENS(AST_Unary, needs_parens_unary); 711 712 PARENS(AST_Sequence, function(output) { 713 var p = output.parent(); 714 // [ 1, (2, 3), 4 ] ---> [ 1, 3, 4 ] 715 return p instanceof AST_Array 716 // () ---> (foo, bar) 717 || is_arrow(p) && p.value === this 718 // await (foo, bar) 719 || p instanceof AST_Await 720 // 1 + (2, 3) + 4 ---> 8 721 || p instanceof AST_Binary 722 // new (foo, bar) or foo(1, (2, 3), 4) 723 || p instanceof AST_Call 724 // class extends (foo, bar) {} 725 // class foo extends (bar, baz) {} 726 || p instanceof AST_Class 727 // class { foo = (bar, baz) } 728 // class { [(foo, bar)]() {} } 729 || p instanceof AST_ClassProperty 730 // (false, true) ? (a = 10, b = 20) : (c = 30) 731 // ---> 20 (side effect, set a := 10 and b := 20) 732 || p instanceof AST_Conditional 733 // [ a = (1, 2) ] = [] ---> a == 2 734 || p instanceof AST_DefaultValue 735 // { [(1, 2)]: foo } = bar 736 // { 1: (2, foo) } = bar 737 || p instanceof AST_DestructuredKeyVal 738 // export default (foo, bar) 739 || p instanceof AST_ExportDefault 740 // for (foo of (bar, baz)); 741 || p instanceof AST_ForOf 742 // { [(1, 2)]: 3 }[2] ---> 3 743 // { foo: (1, 2) }.foo ---> 2 744 || p instanceof AST_ObjectProperty 745 // (1, {foo:2}).foo or (1, {foo:2})["foo"] ---> 2 746 || p instanceof AST_PropAccess && p.expression === this 747 // ...(foo, bar, baz) 748 || p instanceof AST_Spread 749 // (foo, bar)`baz` 750 || p instanceof AST_Template && p.tag === this 751 // !(foo, bar, baz) 752 || p instanceof AST_Unary 753 // var a = (1, 2), b = a + a; ---> b == 4 754 || p instanceof AST_VarDef 755 // yield (foo, bar) 756 || p instanceof AST_Yield; 757 }); 758 759 PARENS(AST_Binary, function(output) { 760 var p = output.parent(); 761 // await (foo && bar) 762 if (p instanceof AST_Await) return true; 763 // this deals with precedence: 764 // 3 * (2 + 1) 765 // 3 - (2 - 1) 766 // (1 ** 2) ** 3 767 if (p instanceof AST_Binary) { 768 var po = p.operator, pp = PRECEDENCE[po]; 769 var so = this.operator, sp = PRECEDENCE[so]; 770 return pp > sp 771 || po == "??" && (so == "&&" || so == "||") 772 || (pp == sp && this === p[po == "**" ? "left" : "right"]); 773 } 774 // (foo && bar)() 775 if (p instanceof AST_Call) return p.expression === this; 776 // class extends (foo && bar) {} 777 // class foo extends (bar || null) {} 778 if (p instanceof AST_Class) return true; 779 // (foo && bar)["prop"], (foo && bar).prop 780 if (p instanceof AST_PropAccess) return p.expression === this; 781 // (foo && bar)`` 782 if (p instanceof AST_Template) return p.tag === this; 783 // typeof (foo && bar) 784 if (p instanceof AST_Unary) return true; 785 }); 786 787 function need_chain_parens(node, parent) { 788 if (!node.terminal) return false; 789 if (!(parent instanceof AST_Call || parent instanceof AST_PropAccess)) return false; 790 return parent.expression === node; 791 } 792 793 PARENS(AST_PropAccess, function(output) { 794 var node = this; 795 var p = output.parent(); 796 // i.e. new (foo().bar) 797 // 798 // if there's one call into this subtree, then we need 799 // parens around it too, otherwise the call will be 800 // interpreted as passing the arguments to the upper New 801 // expression. 802 if (p instanceof AST_New && p.expression === node && root_expr(node).TYPE == "Call") return true; 803 // (foo?.bar)() 804 // (foo?.bar).baz 805 // new (foo?.bar)() 806 return need_chain_parens(node, p); 807 }); 808 809 PARENS(AST_Call, function(output) { 810 var node = this; 811 var p = output.parent(); 812 if (p instanceof AST_New) return p.expression === node; 813 // https://bugs.webkit.org/show_bug.cgi?id=123506 814 if (output.option("webkit") 815 && node.expression instanceof AST_Function 816 && p instanceof AST_PropAccess 817 && p.expression === node) { 818 var g = output.parent(1); 819 if (g instanceof AST_Assign && g.left === p) return true; 820 } 821 // (foo?.())() 822 // (foo?.()).bar 823 // new (foo?.())() 824 return need_chain_parens(node, p); 825 }); 826 827 PARENS(AST_New, function(output) { 828 if (need_constructor_parens(this, output)) return false; 829 var p = output.parent(); 830 // (new foo)(bar) 831 if (p instanceof AST_Call) return p.expression === this; 832 // (new Date).getTime(), (new Date)["getTime"]() 833 if (p instanceof AST_PropAccess) return true; 834 // (new foo)`bar` 835 if (p instanceof AST_Template) return p.tag === this; 836 }); 837 838 PARENS(AST_Number, function(output) { 839 if (!output.option("galio")) return false; 840 // https://github.com/mishoo/UglifyJS/pull/1009 841 var p = output.parent(); 842 return p instanceof AST_PropAccess && p.expression === this && /^0/.test(make_num(this.value)); 843 }); 844 845 function needs_parens_assign_cond(self, output) { 846 var p = output.parent(); 847 // await (a = foo) 848 if (p instanceof AST_Await) return true; 849 // 1 + (a = 2) + 3 → 6, side effect setting a = 2 850 if (p instanceof AST_Binary) return !(p instanceof AST_Assign); 851 // (a = func)() —or— new (a = Object)() 852 if (p instanceof AST_Call) return p.expression === self; 853 // class extends (a = foo) {} 854 // class foo extends (bar ? baz : moo) {} 855 if (p instanceof AST_Class) return true; 856 // (a = foo) ? bar : baz 857 if (p instanceof AST_Conditional) return p.condition === self; 858 // (a = foo)["prop"] —or— (a = foo).prop 859 if (p instanceof AST_PropAccess) return p.expression === self; 860 // (a = foo)`bar` 861 if (p instanceof AST_Template) return p.tag === self; 862 // !(a = false) → true 863 if (p instanceof AST_Unary) return true; 864 } 865 PARENS(AST_Arrow, function(output) { 866 return needs_parens_assign_cond(this, output); 867 }); 868 PARENS(AST_Assign, function(output) { 869 if (needs_parens_assign_cond(this, output)) return true; 870 // v8 parser bug ---> workaround 871 // f([1], [a] = []) ---> f([1], ([a] = [])) 872 if (output.option("v8")) return this.left instanceof AST_Destructured; 873 // ({ p: a } = o); 874 if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output); 875 }); 876 PARENS(AST_AsyncArrow, function(output) { 877 return needs_parens_assign_cond(this, output); 878 }); 879 PARENS(AST_Conditional, function(output) { 880 return needs_parens_assign_cond(this, output) 881 // https://github.com/mishoo/UglifyJS/issues/1144 882 || output.option("extendscript") && output.parent() instanceof AST_Conditional; 883 }); 884 PARENS(AST_Yield, function(output) { 885 return needs_parens_assign_cond(this, output); 886 }); 887 888 /* -----[ PRINTERS ]----- */ 889 890 DEFPRINT(AST_Directive, function(output) { 891 var quote = this.quote; 892 var value = this.value; 893 switch (output.option("quote_style")) { 894 case 0: 895 case 2: 896 if (value.indexOf('"') == -1) quote = '"'; 897 break; 898 case 1: 899 if (value.indexOf("'") == -1) quote = "'"; 900 break; 901 } 902 output.print(quote + value + quote); 903 output.semicolon(); 904 }); 905 DEFPRINT(AST_Debugger, function(output) { 906 output.print("debugger"); 907 output.semicolon(); 908 }); 909 910 /* -----[ statements ]----- */ 911 912 function display_body(body, is_toplevel, output, allow_directives) { 913 var last = body.length - 1; 914 var in_directive = allow_directives; 915 var was_asm = use_asm; 916 body.forEach(function(stmt, i) { 917 if (in_directive) { 918 if (stmt instanceof AST_Directive) { 919 if (stmt.value == "use asm") use_asm = true; 920 } else if (!(stmt instanceof AST_EmptyStatement)) { 921 if (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) { 922 output.force_semicolon(); 923 } 924 in_directive = false; 925 } 926 } 927 if (stmt instanceof AST_EmptyStatement) return; 928 output.indent(); 929 stmt.print(output); 930 if (i == last && is_toplevel) return; 931 output.newline(); 932 if (is_toplevel) output.newline(); 933 }); 934 use_asm = was_asm; 935 } 936 937 DEFPRINT(AST_Toplevel, function(output) { 938 display_body(this.body, true, output, true); 939 output.print(""); 940 }); 941 DEFPRINT(AST_LabeledStatement, function(output) { 942 this.label.print(output); 943 output.colon(); 944 this.body.print(output); 945 }); 946 DEFPRINT(AST_SimpleStatement, function(output) { 947 this.body.print(output); 948 output.semicolon(); 949 }); 950 function print_braced_empty(self, output) { 951 output.print("{"); 952 output.with_indent(function() { 953 output.append_comments(self, true); 954 }); 955 output.print("}"); 956 } 957 function print_braced(self, output, allow_directives) { 958 if (self.body.length > 0) { 959 output.with_block(function() { 960 display_body(self.body, false, output, allow_directives); 961 }, self.end); 962 } else print_braced_empty(self, output); 963 } 964 DEFPRINT(AST_BlockStatement, function(output) { 965 print_braced(this, output); 966 }); 967 DEFPRINT(AST_EmptyStatement, function(output) { 968 output.semicolon(); 969 }); 970 DEFPRINT(AST_Do, function(output) { 971 var self = this; 972 output.print("do"); 973 make_block(self.body, output); 974 output.space(); 975 output.print("while"); 976 output.space(); 977 output.with_parens(function() { 978 self.condition.print(output); 979 }); 980 output.semicolon(); 981 }); 982 DEFPRINT(AST_While, function(output) { 983 var self = this; 984 output.print("while"); 985 output.space(); 986 output.with_parens(function() { 987 self.condition.print(output); 988 }); 989 force_statement(self.body, output); 990 }); 991 DEFPRINT(AST_For, function(output) { 992 var self = this; 993 output.print("for"); 994 output.space(); 995 output.with_parens(function() { 996 if (self.init) { 997 if (self.init instanceof AST_Definitions) { 998 self.init.print(output); 999 } else { 1000 parenthesize_for_no_in(self.init, output, true); 1001 } 1002 output.print(";"); 1003 output.space(); 1004 } else { 1005 output.print(";"); 1006 } 1007 if (self.condition) { 1008 self.condition.print(output); 1009 output.print(";"); 1010 output.space(); 1011 } else { 1012 output.print(";"); 1013 } 1014 if (self.step) { 1015 self.step.print(output); 1016 } 1017 }); 1018 force_statement(self.body, output); 1019 }); 1020 function print_for_enum(prefix, infix) { 1021 return function(output) { 1022 var self = this; 1023 output.print(prefix); 1024 output.space(); 1025 output.with_parens(function() { 1026 self.init.print(output); 1027 output.space(); 1028 output.print(infix); 1029 output.space(); 1030 self.object.print(output); 1031 }); 1032 force_statement(self.body, output); 1033 }; 1034 } 1035 DEFPRINT(AST_ForAwaitOf, print_for_enum("for await", "of")); 1036 DEFPRINT(AST_ForIn, print_for_enum("for", "in")); 1037 DEFPRINT(AST_ForOf, print_for_enum("for", "of")); 1038 DEFPRINT(AST_With, function(output) { 1039 var self = this; 1040 output.print("with"); 1041 output.space(); 1042 output.with_parens(function() { 1043 self.expression.print(output); 1044 }); 1045 force_statement(self.body, output); 1046 }); 1047 DEFPRINT(AST_ExportDeclaration, function(output) { 1048 output.print("export"); 1049 output.space(); 1050 this.body.print(output); 1051 }); 1052 DEFPRINT(AST_ExportDefault, function(output) { 1053 output.print("export"); 1054 output.space(); 1055 output.print("default"); 1056 output.space(); 1057 var body = this.body; 1058 body.print(output); 1059 if (body instanceof AST_ClassExpression) { 1060 if (!body.name) return; 1061 } 1062 if (body instanceof AST_DefClass) return; 1063 if (body instanceof AST_LambdaDefinition) return; 1064 if (body instanceof AST_LambdaExpression) { 1065 if (!body.name && !is_arrow(body)) return; 1066 } 1067 output.semicolon(); 1068 }); 1069 function print_alias(alias, output) { 1070 var value = alias.value; 1071 if (value == "*" || is_identifier_string(value)) { 1072 output.print_name(value); 1073 } else { 1074 output.print_string(value, alias.quote); 1075 } 1076 } 1077 DEFPRINT(AST_ExportForeign, function(output) { 1078 var self = this; 1079 output.print("export"); 1080 output.space(); 1081 var len = self.keys.length; 1082 if (len == 0) { 1083 print_braced_empty(self, output); 1084 } else if (self.keys[0].value == "*") { 1085 print_entry(0); 1086 } else output.with_block(function() { 1087 output.indent(); 1088 print_entry(0); 1089 for (var i = 1; i < len; i++) { 1090 output.print(","); 1091 output.newline(); 1092 output.indent(); 1093 print_entry(i); 1094 } 1095 output.newline(); 1096 }, self.end); 1097 output.space(); 1098 output.print("from"); 1099 output.space(); 1100 self.path.print(output); 1101 output.semicolon(); 1102 1103 function print_entry(index) { 1104 var alias = self.aliases[index]; 1105 var key = self.keys[index]; 1106 print_alias(key, output); 1107 if (alias.value != key.value) { 1108 output.space(); 1109 output.print("as"); 1110 output.space(); 1111 print_alias(alias, output); 1112 } 1113 } 1114 }); 1115 DEFPRINT(AST_ExportReferences, function(output) { 1116 var self = this; 1117 output.print("export"); 1118 output.space(); 1119 print_properties(self, output); 1120 output.semicolon(); 1121 }); 1122 DEFPRINT(AST_Import, function(output) { 1123 var self = this; 1124 output.print("import"); 1125 output.space(); 1126 if (self.default) self.default.print(output); 1127 if (self.all) { 1128 if (self.default) output.comma(); 1129 self.all.print(output); 1130 } 1131 if (self.properties) { 1132 if (self.default) output.comma(); 1133 print_properties(self, output); 1134 } 1135 if (self.all || self.default || self.properties) { 1136 output.space(); 1137 output.print("from"); 1138 output.space(); 1139 } 1140 self.path.print(output); 1141 output.semicolon(); 1142 }); 1143 1144 /* -----[ functions ]----- */ 1145 function print_funargs(self, output) { 1146 output.with_parens(function() { 1147 self.argnames.forEach(function(arg, i) { 1148 if (i) output.comma(); 1149 arg.print(output); 1150 }); 1151 if (self.rest) { 1152 if (self.argnames.length) output.comma(); 1153 output.print("..."); 1154 self.rest.print(output); 1155 } 1156 }); 1157 } 1158 function print_arrow(self, output) { 1159 var argname = self.argnames.length == 1 && !self.rest && self.argnames[0]; 1160 if (argname instanceof AST_SymbolFunarg && argname.name != "yield") { 1161 argname.print(output); 1162 } else { 1163 print_funargs(self, output); 1164 } 1165 output.space(); 1166 output.print("=>"); 1167 output.space(); 1168 if (self.value) { 1169 self.value.print(output); 1170 } else { 1171 print_braced(self, output, true); 1172 } 1173 } 1174 DEFPRINT(AST_Arrow, function(output) { 1175 print_arrow(this, output); 1176 }); 1177 DEFPRINT(AST_AsyncArrow, function(output) { 1178 output.print("async"); 1179 output.space(); 1180 print_arrow(this, output); 1181 }); 1182 function print_lambda(self, output) { 1183 if (self.name) { 1184 output.space(); 1185 self.name.print(output); 1186 } 1187 print_funargs(self, output); 1188 output.space(); 1189 print_braced(self, output, true); 1190 } 1191 DEFPRINT(AST_Lambda, function(output) { 1192 output.print("function"); 1193 print_lambda(this, output); 1194 }); 1195 function print_async(output) { 1196 output.print("async"); 1197 output.space(); 1198 output.print("function"); 1199 print_lambda(this, output); 1200 } 1201 DEFPRINT(AST_AsyncDefun, print_async); 1202 DEFPRINT(AST_AsyncFunction, print_async); 1203 function print_async_generator(output) { 1204 output.print("async"); 1205 output.space(); 1206 output.print("function*"); 1207 print_lambda(this, output); 1208 } 1209 DEFPRINT(AST_AsyncGeneratorDefun, print_async_generator); 1210 DEFPRINT(AST_AsyncGeneratorFunction, print_async_generator); 1211 function print_generator(output) { 1212 output.print("function*"); 1213 print_lambda(this, output); 1214 } 1215 DEFPRINT(AST_GeneratorDefun, print_generator); 1216 DEFPRINT(AST_GeneratorFunction, print_generator); 1217 1218 /* -----[ classes ]----- */ 1219 DEFPRINT(AST_Class, function(output) { 1220 var self = this; 1221 output.print("class"); 1222 if (self.name) { 1223 output.space(); 1224 self.name.print(output); 1225 } 1226 if (self.extends) { 1227 output.space(); 1228 output.print("extends"); 1229 output.space(); 1230 self.extends.print(output); 1231 } 1232 output.space(); 1233 print_properties(self, output, true); 1234 }); 1235 DEFPRINT(AST_ClassField, function(output) { 1236 var self = this; 1237 if (self.static) { 1238 output.print("static"); 1239 output.space(); 1240 } 1241 print_property_key(self, output); 1242 if (self.value) { 1243 output.space(); 1244 output.print("="); 1245 output.space(); 1246 self.value.print(output); 1247 } 1248 output.semicolon(); 1249 }); 1250 DEFPRINT(AST_ClassGetter, print_accessor("get")); 1251 DEFPRINT(AST_ClassSetter, print_accessor("set")); 1252 function print_method(self, output) { 1253 var fn = self.value; 1254 if (is_async(fn)) { 1255 output.print("async"); 1256 output.space(); 1257 } 1258 if (is_generator(fn)) output.print("*"); 1259 print_property_key(self, output); 1260 print_lambda(self.value, output); 1261 } 1262 DEFPRINT(AST_ClassMethod, function(output) { 1263 var self = this; 1264 if (self.static) { 1265 output.print("static"); 1266 output.space(); 1267 } 1268 print_method(self, output); 1269 }); 1270 DEFPRINT(AST_ClassInit, function(output) { 1271 output.print("static"); 1272 output.space(); 1273 print_braced(this.value, output); 1274 }); 1275 1276 /* -----[ jumps ]----- */ 1277 function print_jump(kind, prop) { 1278 return function(output) { 1279 output.print(kind); 1280 var target = this[prop]; 1281 if (target) { 1282 output.space(); 1283 target.print(output); 1284 } 1285 output.semicolon(); 1286 }; 1287 } 1288 DEFPRINT(AST_Return, print_jump("return", "value")); 1289 DEFPRINT(AST_Throw, print_jump("throw", "value")); 1290 DEFPRINT(AST_Break, print_jump("break", "label")); 1291 DEFPRINT(AST_Continue, print_jump("continue", "label")); 1292 1293 /* -----[ if ]----- */ 1294 function make_then(self, output) { 1295 var b = self.body; 1296 if (output.option("braces") && !(b instanceof AST_Const || b instanceof AST_Let) 1297 || output.option("ie") && b instanceof AST_Do) 1298 return make_block(b, output); 1299 // The squeezer replaces "block"-s that contain only a single 1300 // statement with the statement itself; technically, the AST 1301 // is correct, but this can create problems when we output an 1302 // IF having an ELSE clause where the THEN clause ends in an 1303 // IF *without* an ELSE block (then the outer ELSE would refer 1304 // to the inner IF). This function checks for this case and 1305 // adds the block braces if needed. 1306 if (!b) return output.force_semicolon(); 1307 while (true) { 1308 if (b instanceof AST_If) { 1309 if (!b.alternative) { 1310 make_block(self.body, output); 1311 return; 1312 } 1313 b = b.alternative; 1314 } else if (b instanceof AST_StatementWithBody) { 1315 b = b.body; 1316 } else break; 1317 } 1318 force_statement(self.body, output); 1319 } 1320 DEFPRINT(AST_If, function(output) { 1321 var self = this; 1322 output.print("if"); 1323 output.space(); 1324 output.with_parens(function() { 1325 self.condition.print(output); 1326 }); 1327 if (self.alternative) { 1328 make_then(self, output); 1329 output.space(); 1330 output.print("else"); 1331 if (self.alternative instanceof AST_If) { 1332 output.space(); 1333 self.alternative.print(output); 1334 } else { 1335 force_statement(self.alternative, output); 1336 } 1337 } else { 1338 force_statement(self.body, output); 1339 } 1340 }); 1341 1342 /* -----[ switch ]----- */ 1343 DEFPRINT(AST_Switch, function(output) { 1344 var self = this; 1345 output.print("switch"); 1346 output.space(); 1347 output.with_parens(function() { 1348 self.expression.print(output); 1349 }); 1350 output.space(); 1351 var last = self.body.length - 1; 1352 if (last < 0) print_braced_empty(self, output); 1353 else output.with_block(function() { 1354 self.body.forEach(function(branch, i) { 1355 output.indent(true); 1356 branch.print(output); 1357 if (i < last && branch.body.length > 0) 1358 output.newline(); 1359 }); 1360 }, self.end); 1361 }); 1362 function print_branch_body(self, output) { 1363 output.newline(); 1364 self.body.forEach(function(stmt) { 1365 output.indent(); 1366 stmt.print(output); 1367 output.newline(); 1368 }); 1369 } 1370 DEFPRINT(AST_Default, function(output) { 1371 output.print("default:"); 1372 print_branch_body(this, output); 1373 }); 1374 DEFPRINT(AST_Case, function(output) { 1375 var self = this; 1376 output.print("case"); 1377 output.space(); 1378 self.expression.print(output); 1379 output.print(":"); 1380 print_branch_body(self, output); 1381 }); 1382 1383 /* -----[ exceptions ]----- */ 1384 DEFPRINT(AST_Try, function(output) { 1385 var self = this; 1386 output.print("try"); 1387 output.space(); 1388 print_braced(self, output); 1389 if (self.bcatch) { 1390 output.space(); 1391 self.bcatch.print(output); 1392 } 1393 if (self.bfinally) { 1394 output.space(); 1395 self.bfinally.print(output); 1396 } 1397 }); 1398 DEFPRINT(AST_Catch, function(output) { 1399 var self = this; 1400 output.print("catch"); 1401 if (self.argname) { 1402 output.space(); 1403 output.with_parens(function() { 1404 self.argname.print(output); 1405 }); 1406 } 1407 output.space(); 1408 print_braced(self, output); 1409 }); 1410 DEFPRINT(AST_Finally, function(output) { 1411 output.print("finally"); 1412 output.space(); 1413 print_braced(this, output); 1414 }); 1415 1416 function print_definitions(type) { 1417 return function(output) { 1418 var self = this; 1419 output.print(type); 1420 output.space(); 1421 self.definitions.forEach(function(def, i) { 1422 if (i) output.comma(); 1423 def.print(output); 1424 }); 1425 var p = output.parent(); 1426 if (!(p instanceof AST_IterationStatement && p.init === self)) output.semicolon(); 1427 }; 1428 } 1429 DEFPRINT(AST_Const, print_definitions("const")); 1430 DEFPRINT(AST_Let, print_definitions("let")); 1431 DEFPRINT(AST_Var, print_definitions("var")); 1432 1433 function parenthesize_for_no_in(node, output, no_in) { 1434 var parens = false; 1435 // need to take some precautions here: 1436 // https://github.com/mishoo/UglifyJS/issues/60 1437 if (no_in) node.walk(new TreeWalker(function(node) { 1438 if (parens) return true; 1439 if (node instanceof AST_Binary && node.operator == "in") return parens = true; 1440 if (node instanceof AST_Scope && !(is_arrow(node) && node.value)) return true; 1441 })); 1442 node.print(output, parens); 1443 } 1444 1445 DEFPRINT(AST_VarDef, function(output) { 1446 var self = this; 1447 self.name.print(output); 1448 if (self.value) { 1449 output.space(); 1450 output.print("="); 1451 output.space(); 1452 var p = output.parent(1); 1453 var no_in = p instanceof AST_For || p instanceof AST_ForEnumeration; 1454 parenthesize_for_no_in(self.value, output, no_in); 1455 } 1456 }); 1457 1458 DEFPRINT(AST_DefaultValue, function(output) { 1459 var self = this; 1460 self.name.print(output); 1461 output.space(); 1462 output.print("="); 1463 output.space(); 1464 self.value.print(output); 1465 }); 1466 1467 /* -----[ other expressions ]----- */ 1468 function print_annotation(self, output) { 1469 if (!output.option("annotations")) return; 1470 if (!self.pure) return; 1471 var level = 0, parent = self, node; 1472 do { 1473 node = parent; 1474 parent = output.parent(level++); 1475 if (parent instanceof AST_Call && parent.expression === node) return; 1476 } while (parent instanceof AST_PropAccess && parent.expression === node); 1477 output.print("/*@__PURE__*/"); 1478 } 1479 function print_call_args(self, output) { 1480 output.with_parens(function() { 1481 self.args.forEach(function(expr, i) { 1482 if (i) output.comma(); 1483 expr.print(output); 1484 }); 1485 output.add_mapping(self.end); 1486 }); 1487 } 1488 DEFPRINT(AST_Call, function(output) { 1489 var self = this; 1490 print_annotation(self, output); 1491 self.expression.print(output); 1492 if (self.optional) output.print("?."); 1493 print_call_args(self, output); 1494 }); 1495 DEFPRINT(AST_New, function(output) { 1496 var self = this; 1497 print_annotation(self, output); 1498 output.print("new"); 1499 output.space(); 1500 self.expression.print(output); 1501 if (need_constructor_parens(self, output)) print_call_args(self, output); 1502 }); 1503 DEFPRINT(AST_Sequence, function(output) { 1504 this.expressions.forEach(function(node, index) { 1505 if (index > 0) { 1506 output.comma(); 1507 if (output.should_break()) { 1508 output.newline(); 1509 output.indent(); 1510 } 1511 } 1512 node.print(output); 1513 }); 1514 }); 1515 DEFPRINT(AST_Dot, function(output) { 1516 var self = this; 1517 var expr = self.expression; 1518 expr.print(output); 1519 var prop = self.property; 1520 if (output.option("ie") && RESERVED_WORDS[prop] || self.quoted && output.option("keep_quoted_props")) { 1521 if (self.optional) output.print("?."); 1522 output.with_square(function() { 1523 output.add_mapping(self.end); 1524 output.print_string(prop); 1525 }); 1526 } else { 1527 if (expr instanceof AST_Number && !/[ex.)]/i.test(output.last())) output.print("."); 1528 output.print(self.optional ? "?." : "."); 1529 // the name after dot would be mapped about here. 1530 output.add_mapping(self.end); 1531 output.print_name(prop); 1532 } 1533 }); 1534 DEFPRINT(AST_Sub, function(output) { 1535 var self = this; 1536 self.expression.print(output); 1537 if (self.optional) output.print("?."); 1538 output.with_square(function() { 1539 self.property.print(output); 1540 }); 1541 }); 1542 DEFPRINT(AST_Spread, function(output) { 1543 output.print("..."); 1544 this.expression.print(output); 1545 }); 1546 DEFPRINT(AST_UnaryPrefix, function(output) { 1547 var op = this.operator; 1548 var exp = this.expression; 1549 output.print(op); 1550 if (/^[a-z]/i.test(op) 1551 || (/[+-]$/.test(op) 1552 && exp instanceof AST_UnaryPrefix 1553 && /^[+-]/.test(exp.operator))) { 1554 output.space(); 1555 } 1556 exp.print(output); 1557 }); 1558 DEFPRINT(AST_UnaryPostfix, function(output) { 1559 var self = this; 1560 self.expression.print(output); 1561 output.add_mapping(self.end); 1562 output.print(self.operator); 1563 }); 1564 DEFPRINT(AST_Binary, function(output) { 1565 var self = this; 1566 self.left.print(output); 1567 output.space(); 1568 output.print(self.operator); 1569 output.space(); 1570 self.right.print(output); 1571 }); 1572 DEFPRINT(AST_Conditional, function(output) { 1573 var self = this; 1574 self.condition.print(output); 1575 output.space(); 1576 output.print("?"); 1577 output.space(); 1578 self.consequent.print(output); 1579 output.space(); 1580 output.colon(); 1581 self.alternative.print(output); 1582 }); 1583 DEFPRINT(AST_Await, function(output) { 1584 output.print("await"); 1585 output.space(); 1586 this.expression.print(output); 1587 }); 1588 DEFPRINT(AST_Yield, function(output) { 1589 output.print(this.nested ? "yield*" : "yield"); 1590 if (this.expression) { 1591 output.space(); 1592 this.expression.print(output); 1593 } 1594 }); 1595 1596 /* -----[ literals ]----- */ 1597 DEFPRINT(AST_Array, function(output) { 1598 var a = this.elements, len = a.length; 1599 output.with_square(len > 0 ? function() { 1600 output.space(); 1601 a.forEach(function(exp, i) { 1602 if (i) output.comma(); 1603 exp.print(output); 1604 // If the final element is a hole, we need to make sure it 1605 // doesn't look like a trailing comma, by inserting an actual 1606 // trailing comma. 1607 if (i === len - 1 && exp instanceof AST_Hole) 1608 output.comma(); 1609 }); 1610 output.space(); 1611 } : noop); 1612 }); 1613 DEFPRINT(AST_DestructuredArray, function(output) { 1614 var a = this.elements, len = a.length, rest = this.rest; 1615 output.with_square(len || rest ? function() { 1616 output.space(); 1617 a.forEach(function(exp, i) { 1618 if (i) output.comma(); 1619 exp.print(output); 1620 }); 1621 if (rest) { 1622 if (len) output.comma(); 1623 output.print("..."); 1624 rest.print(output); 1625 } else if (a[len - 1] instanceof AST_Hole) { 1626 // If the final element is a hole, we need to make sure it 1627 // doesn't look like a trailing comma, by inserting an actual 1628 // trailing comma. 1629 output.comma(); 1630 } 1631 output.space(); 1632 } : noop); 1633 }); 1634 DEFPRINT(AST_DestructuredKeyVal, function(output) { 1635 var self = this; 1636 var key = print_property_key(self, output); 1637 var value = self.value; 1638 if (key) { 1639 if (value instanceof AST_DefaultValue) { 1640 if (value.name instanceof AST_Symbol && key == get_symbol_name(value.name)) { 1641 output.space(); 1642 output.print("="); 1643 output.space(); 1644 value.value.print(output); 1645 return; 1646 } 1647 } else if (value instanceof AST_Symbol) { 1648 if (key == get_symbol_name(value)) return; 1649 } 1650 } 1651 output.colon(); 1652 value.print(output); 1653 }); 1654 DEFPRINT(AST_DestructuredObject, function(output) { 1655 var self = this; 1656 var props = self.properties, len = props.length, rest = self.rest; 1657 if (len || rest) output.with_block(function() { 1658 props.forEach(function(prop, i) { 1659 if (i) { 1660 output.print(","); 1661 output.newline(); 1662 } 1663 output.indent(); 1664 prop.print(output); 1665 }); 1666 if (rest) { 1667 if (len) { 1668 output.print(","); 1669 output.newline(); 1670 } 1671 output.indent(); 1672 output.print("..."); 1673 rest.print(output); 1674 } 1675 output.newline(); 1676 }, self.end); 1677 else print_braced_empty(self, output); 1678 }); 1679 function print_properties(self, output, no_comma) { 1680 var props = self.properties; 1681 if (props.length > 0) output.with_block(function() { 1682 props.forEach(function(prop, i) { 1683 if (i) { 1684 if (!no_comma) output.print(","); 1685 output.newline(); 1686 } 1687 output.indent(); 1688 prop.print(output); 1689 }); 1690 output.newline(); 1691 }, self.end); 1692 else print_braced_empty(self, output); 1693 } 1694 DEFPRINT(AST_Object, function(output) { 1695 print_properties(this, output); 1696 }); 1697 1698 function print_property_key(self, output) { 1699 var key = self.key; 1700 if (key instanceof AST_Node) return output.with_square(function() { 1701 key.print(output); 1702 }); 1703 var quote = self.start && self.start.quote; 1704 if (output.option("quote_keys") || quote && output.option("keep_quoted_props")) { 1705 output.print_string(key, quote); 1706 } else if ("" + +key == key && key >= 0) { 1707 output.print(make_num(key)); 1708 } else if (self.private) { 1709 output.print_name(key); 1710 } else if (RESERVED_WORDS[key] ? !output.option("ie") : is_identifier_string(key)) { 1711 output.print_name(key); 1712 return key; 1713 } else { 1714 output.print_string(key, quote); 1715 } 1716 } 1717 DEFPRINT(AST_ObjectKeyVal, function(output) { 1718 var self = this; 1719 print_property_key(self, output); 1720 output.colon(); 1721 self.value.print(output); 1722 }); 1723 DEFPRINT(AST_ObjectMethod, function(output) { 1724 print_method(this, output); 1725 }); 1726 function print_accessor(type) { 1727 return function(output) { 1728 var self = this; 1729 if (self.static) { 1730 output.print("static"); 1731 output.space(); 1732 } 1733 output.print(type); 1734 output.space(); 1735 print_property_key(self, output); 1736 print_lambda(self.value, output); 1737 }; 1738 } 1739 DEFPRINT(AST_ObjectGetter, print_accessor("get")); 1740 DEFPRINT(AST_ObjectSetter, print_accessor("set")); 1741 function get_symbol_name(sym) { 1742 var def = sym.definition(); 1743 return def && def.mangled_name || sym.name; 1744 } 1745 DEFPRINT(AST_Symbol, function(output) { 1746 output.print_name(get_symbol_name(this)); 1747 }); 1748 DEFPRINT(AST_SymbolExport, function(output) { 1749 var self = this; 1750 var name = get_symbol_name(self); 1751 output.print_name(name); 1752 var alias = self.alias; 1753 if (alias.value != name) { 1754 output.space(); 1755 output.print("as"); 1756 output.space(); 1757 print_alias(alias, output); 1758 } 1759 }); 1760 DEFPRINT(AST_SymbolImport, function(output) { 1761 var self = this; 1762 var name = get_symbol_name(self); 1763 var key = self.key; 1764 if (key.value && key.value != name) { 1765 print_alias(key, output); 1766 output.space(); 1767 output.print("as"); 1768 output.space(); 1769 } 1770 output.print_name(name); 1771 }); 1772 DEFPRINT(AST_Hole, noop); 1773 DEFPRINT(AST_Template, function(output) { 1774 var self = this; 1775 if (self.tag) self.tag.print(output); 1776 output.print("`"); 1777 for (var i = 0; i < self.expressions.length; i++) { 1778 output.print(self.strings[i]); 1779 output.print("${"); 1780 self.expressions[i].print(output); 1781 output.print("}"); 1782 } 1783 output.print(self.strings[i]); 1784 output.print("`"); 1785 }); 1786 DEFPRINT(AST_Constant, function(output) { 1787 output.print("" + this.value); 1788 }); 1789 DEFPRINT(AST_String, function(output) { 1790 output.print_string(this.value, this.quote); 1791 }); 1792 DEFPRINT(AST_Number, function(output) { 1793 var start = this.start; 1794 if (use_asm && start && start.raw != null) { 1795 output.print(start.raw); 1796 } else { 1797 output.print(make_num(this.value)); 1798 } 1799 }); 1800 1801 DEFPRINT(AST_RegExp, function(output) { 1802 var regexp = this.value; 1803 var str = regexp.toString(); 1804 var end = str.lastIndexOf("/"); 1805 if (regexp.raw_source) { 1806 str = "/" + regexp.raw_source + str.slice(end); 1807 } else if (end == 1) { 1808 str = "/(?:)" + str.slice(end); 1809 } else if (str.indexOf("/", 1) < end) { 1810 str = "/" + str.slice(1, end).replace(/\\\\|[^/]?\//g, function(match) { 1811 return match[0] == "\\" ? match : match.slice(0, -1) + "\\/"; 1812 }) + str.slice(end); 1813 } 1814 output.print(output.to_utf8(str).replace(/\\(?:\0(?![0-9])|[^\0])/g, function(match) { 1815 switch (match[1]) { 1816 case "\n": return "\\n"; 1817 case "\r": return "\\r"; 1818 case "\t": return "\t"; 1819 case "\b": return "\b"; 1820 case "\f": return "\f"; 1821 case "\0": return "\0"; 1822 case "\x0B": return "\v"; 1823 case "\u2028": return "\\u2028"; 1824 case "\u2029": return "\\u2029"; 1825 default: return match; 1826 } 1827 }).replace(/[\n\r\u2028\u2029]/g, function(c) { 1828 switch (c) { 1829 case "\n": return "\\n"; 1830 case "\r": return "\\r"; 1831 case "\u2028": return "\\u2028"; 1832 case "\u2029": return "\\u2029"; 1833 } 1834 })); 1835 }); 1836 1837 function force_statement(stat, output) { 1838 if (output.option("braces") && !(stat instanceof AST_Const || stat instanceof AST_Let)) { 1839 make_block(stat, output); 1840 } else if (stat instanceof AST_EmptyStatement) { 1841 output.force_semicolon(); 1842 } else { 1843 output.space(); 1844 stat.print(output); 1845 } 1846 } 1847 1848 // self should be AST_New. decide if we want to show parens or not. 1849 function need_constructor_parens(self, output) { 1850 // Always print parentheses with arguments 1851 if (self.args.length > 0) return true; 1852 1853 return output.option("beautify"); 1854 } 1855 1856 function best_of(a) { 1857 var best = a[0], len = best.length; 1858 for (var i = 1; i < a.length; ++i) { 1859 if (a[i].length < len) { 1860 best = a[i]; 1861 len = best.length; 1862 } 1863 } 1864 return best; 1865 } 1866 1867 function make_num(num) { 1868 var str = num.toString(10).replace(/^0\./, ".").replace("e+", "e"); 1869 var candidates = [ str ]; 1870 if (Math.floor(num) === num) { 1871 if (num < 0) { 1872 candidates.push("-0x" + (-num).toString(16).toLowerCase()); 1873 } else { 1874 candidates.push("0x" + num.toString(16).toLowerCase()); 1875 } 1876 } 1877 var match, len, digits; 1878 if (match = /^\.0+/.exec(str)) { 1879 len = match[0].length; 1880 digits = str.slice(len); 1881 candidates.push(digits + "e-" + (digits.length + len - 1)); 1882 } else if (match = /[^0]0+$/.exec(str)) { 1883 len = match[0].length - 1; 1884 candidates.push(str.slice(0, -len) + "e" + len); 1885 } else if (match = /^(\d)\.(\d+)e(-?\d+)$/.exec(str)) { 1886 candidates.push(match[1] + match[2] + "e" + (match[3] - match[2].length)); 1887 } 1888 return best_of(candidates); 1889 } 1890 1891 function make_block(stmt, output) { 1892 output.space(); 1893 if (stmt instanceof AST_EmptyStatement) { 1894 print_braced_empty(stmt, output); 1895 } else if (stmt instanceof AST_BlockStatement) { 1896 stmt.print(output); 1897 } else output.with_block(function() { 1898 output.indent(); 1899 stmt.print(output); 1900 output.newline(); 1901 }, stmt.end); 1902 } 1903 1904 /* -----[ source map generators ]----- */ 1905 1906 function DEFMAP(nodetype, generator) { 1907 nodetype.forEach(function(nodetype) { 1908 nodetype.DEFMETHOD("add_source_map", generator); 1909 }); 1910 } 1911 1912 DEFMAP([ 1913 // We could easily add info for ALL nodes, but it seems to me that 1914 // would be quite wasteful, hence this noop in the base class. 1915 AST_Node, 1916 // since the label symbol will mark it 1917 AST_LabeledStatement, 1918 ], noop); 1919 1920 // XXX: I'm not exactly sure if we need it for all of these nodes, 1921 // or if we should add even more. 1922 DEFMAP([ 1923 AST_Array, 1924 AST_Await, 1925 AST_BlockStatement, 1926 AST_Catch, 1927 AST_Constant, 1928 AST_Debugger, 1929 AST_Definitions, 1930 AST_Destructured, 1931 AST_Directive, 1932 AST_Finally, 1933 AST_Jump, 1934 AST_Lambda, 1935 AST_New, 1936 AST_Object, 1937 AST_Spread, 1938 AST_StatementWithBody, 1939 AST_Symbol, 1940 AST_Switch, 1941 AST_SwitchBranch, 1942 AST_Try, 1943 AST_UnaryPrefix, 1944 AST_Yield, 1945 ], function(output) { 1946 output.add_mapping(this.start); 1947 }); 1948 1949 DEFMAP([ 1950 AST_ClassProperty, 1951 AST_DestructuredKeyVal, 1952 AST_ObjectProperty, 1953 ], function(output) { 1954 if (typeof this.key == "string") output.add_mapping(this.start, this.key); 1955 }); 1956})(); 1957