UNPKG

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