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 if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg && !self.rest) {
1156 self.argnames[0].print(output);
1157 } else {
1158 print_funargs(self, output);
1159 }
1160 output.space();
1161 output.print("=>");
1162 output.space();
1163 if (self.value) {
1164 self.value.print(output);
1165 } else {
1166 print_braced(self, output, true);
1167 }
1168 }
1169 DEFPRINT(AST_Arrow, function(output) {
1170 print_arrow(this, output);
1171 });
1172 DEFPRINT(AST_AsyncArrow, function(output) {
1173 output.print("async");
1174 output.space();
1175 print_arrow(this, output);
1176 });
1177 function print_lambda(self, output) {
1178 if (self.name) {
1179 output.space();
1180 self.name.print(output);
1181 }
1182 print_funargs(self, output);
1183 output.space();
1184 print_braced(self, output, true);
1185 }
1186 DEFPRINT(AST_Lambda, function(output) {
1187 output.print("function");
1188 print_lambda(this, output);
1189 });
1190 function print_async(output) {
1191 output.print("async");
1192 output.space();
1193 output.print("function");
1194 print_lambda(this, output);
1195 }
1196 DEFPRINT(AST_AsyncDefun, print_async);
1197 DEFPRINT(AST_AsyncFunction, print_async);
1198 function print_async_generator(output) {
1199 output.print("async");
1200 output.space();
1201 output.print("function*");
1202 print_lambda(this, output);
1203 }
1204 DEFPRINT(AST_AsyncGeneratorDefun, print_async_generator);
1205 DEFPRINT(AST_AsyncGeneratorFunction, print_async_generator);
1206 function print_generator(output) {
1207 output.print("function*");
1208 print_lambda(this, output);
1209 }
1210 DEFPRINT(AST_GeneratorDefun, print_generator);
1211 DEFPRINT(AST_GeneratorFunction, print_generator);
1212
1213 /* -----[ classes ]----- */
1214 DEFPRINT(AST_Class, function(output) {
1215 var self = this;
1216 output.print("class");
1217 if (self.name) {
1218 output.space();
1219 self.name.print(output);
1220 }
1221 if (self.extends) {
1222 output.space();
1223 output.print("extends");
1224 output.space();
1225 self.extends.print(output);
1226 }
1227 output.space();
1228 print_properties(self, output, true);
1229 });
1230 DEFPRINT(AST_ClassField, function(output) {
1231 var self = this;
1232 if (self.static) {
1233 output.print("static");
1234 output.space();
1235 }
1236 print_property_key(self, output);
1237 if (self.value) {
1238 output.space();
1239 output.print("=");
1240 output.space();
1241 self.value.print(output);
1242 }
1243 output.semicolon();
1244 });
1245 DEFPRINT(AST_ClassGetter, print_accessor("get"));
1246 DEFPRINT(AST_ClassSetter, print_accessor("set"));
1247 function print_method(self, output) {
1248 var fn = self.value;
1249 if (is_async(fn)) {
1250 output.print("async");
1251 output.space();
1252 }
1253 if (is_generator(fn)) output.print("*");
1254 print_property_key(self, output);
1255 print_lambda(self.value, output);
1256 }
1257 DEFPRINT(AST_ClassMethod, function(output) {
1258 var self = this;
1259 if (self.static) {
1260 output.print("static");
1261 output.space();
1262 }
1263 print_method(self, output);
1264 });
1265
1266 /* -----[ jumps ]----- */
1267 function print_jump(kind, prop) {
1268 return function(output) {
1269 output.print(kind);
1270 var target = this[prop];
1271 if (target) {
1272 output.space();
1273 target.print(output);
1274 }
1275 output.semicolon();
1276 };
1277 }
1278 DEFPRINT(AST_Return, print_jump("return", "value"));
1279 DEFPRINT(AST_Throw, print_jump("throw", "value"));
1280 DEFPRINT(AST_Break, print_jump("break", "label"));
1281 DEFPRINT(AST_Continue, print_jump("continue", "label"));
1282
1283 /* -----[ if ]----- */
1284 function make_then(self, output) {
1285 var b = self.body;
1286 if (output.option("braces") && !(b instanceof AST_Const || b instanceof AST_Let)
1287 || output.option("ie") && b instanceof AST_Do)
1288 return make_block(b, output);
1289 // The squeezer replaces "block"-s that contain only a single
1290 // statement with the statement itself; technically, the AST
1291 // is correct, but this can create problems when we output an
1292 // IF having an ELSE clause where the THEN clause ends in an
1293 // IF *without* an ELSE block (then the outer ELSE would refer
1294 // to the inner IF). This function checks for this case and
1295 // adds the block braces if needed.
1296 if (!b) return output.force_semicolon();
1297 while (true) {
1298 if (b instanceof AST_If) {
1299 if (!b.alternative) {
1300 make_block(self.body, output);
1301 return;
1302 }
1303 b = b.alternative;
1304 } else if (b instanceof AST_StatementWithBody) {
1305 b = b.body;
1306 } else break;
1307 }
1308 force_statement(self.body, output);
1309 }
1310 DEFPRINT(AST_If, function(output) {
1311 var self = this;
1312 output.print("if");
1313 output.space();
1314 output.with_parens(function() {
1315 self.condition.print(output);
1316 });
1317 if (self.alternative) {
1318 make_then(self, output);
1319 output.space();
1320 output.print("else");
1321 if (self.alternative instanceof AST_If) {
1322 output.space();
1323 self.alternative.print(output);
1324 } else {
1325 force_statement(self.alternative, output);
1326 }
1327 } else {
1328 force_statement(self.body, output);
1329 }
1330 });
1331
1332 /* -----[ switch ]----- */
1333 DEFPRINT(AST_Switch, function(output) {
1334 var self = this;
1335 output.print("switch");
1336 output.space();
1337 output.with_parens(function() {
1338 self.expression.print(output);
1339 });
1340 output.space();
1341 var last = self.body.length - 1;
1342 if (last < 0) print_braced_empty(self, output);
1343 else output.with_block(function() {
1344 self.body.forEach(function(branch, i) {
1345 output.indent(true);
1346 branch.print(output);
1347 if (i < last && branch.body.length > 0)
1348 output.newline();
1349 });
1350 });
1351 });
1352 function print_branch_body(self, output) {
1353 output.newline();
1354 self.body.forEach(function(stmt) {
1355 output.indent();
1356 stmt.print(output);
1357 output.newline();
1358 });
1359 }
1360 DEFPRINT(AST_Default, function(output) {
1361 output.print("default:");
1362 print_branch_body(this, output);
1363 });
1364 DEFPRINT(AST_Case, function(output) {
1365 var self = this;
1366 output.print("case");
1367 output.space();
1368 self.expression.print(output);
1369 output.print(":");
1370 print_branch_body(self, output);
1371 });
1372
1373 /* -----[ exceptions ]----- */
1374 DEFPRINT(AST_Try, function(output) {
1375 var self = this;
1376 output.print("try");
1377 output.space();
1378 print_braced(self, output);
1379 if (self.bcatch) {
1380 output.space();
1381 self.bcatch.print(output);
1382 }
1383 if (self.bfinally) {
1384 output.space();
1385 self.bfinally.print(output);
1386 }
1387 });
1388 DEFPRINT(AST_Catch, function(output) {
1389 var self = this;
1390 output.print("catch");
1391 if (self.argname) {
1392 output.space();
1393 output.with_parens(function() {
1394 self.argname.print(output);
1395 });
1396 }
1397 output.space();
1398 print_braced(self, output);
1399 });
1400 DEFPRINT(AST_Finally, function(output) {
1401 output.print("finally");
1402 output.space();
1403 print_braced(this, output);
1404 });
1405
1406 function print_definitinos(type) {
1407 return function(output) {
1408 var self = this;
1409 output.print(type);
1410 output.space();
1411 self.definitions.forEach(function(def, i) {
1412 if (i) output.comma();
1413 def.print(output);
1414 });
1415 var p = output.parent();
1416 if (!(p instanceof AST_IterationStatement && p.init === self)) output.semicolon();
1417 };
1418 }
1419 DEFPRINT(AST_Const, print_definitinos("const"));
1420 DEFPRINT(AST_Let, print_definitinos("let"));
1421 DEFPRINT(AST_Var, print_definitinos("var"));
1422
1423 function parenthesize_for_noin(node, output, noin) {
1424 var parens = false;
1425 // need to take some precautions here:
1426 // https://github.com/mishoo/UglifyJS/issues/60
1427 if (noin) node.walk(new TreeWalker(function(node) {
1428 if (parens) return true;
1429 if (node instanceof AST_Binary && node.operator == "in") return parens = true;
1430 if (node instanceof AST_Scope && !(is_arrow(node) && node.value)) return true;
1431 }));
1432 node.print(output, parens);
1433 }
1434
1435 DEFPRINT(AST_VarDef, function(output) {
1436 var self = this;
1437 self.name.print(output);
1438 if (self.value) {
1439 output.space();
1440 output.print("=");
1441 output.space();
1442 var p = output.parent(1);
1443 var noin = p instanceof AST_For || p instanceof AST_ForEnumeration;
1444 parenthesize_for_noin(self.value, output, noin);
1445 }
1446 });
1447
1448 DEFPRINT(AST_DefaultValue, function(output) {
1449 var self = this;
1450 self.name.print(output);
1451 output.space();
1452 output.print("=");
1453 output.space();
1454 self.value.print(output);
1455 });
1456
1457 /* -----[ other expressions ]----- */
1458 function print_annotation(self, output) {
1459 if (!output.option("annotations")) return;
1460 if (!self.pure) return;
1461 var level = 0, parent = self, node;
1462 do {
1463 node = parent;
1464 parent = output.parent(level++);
1465 if (parent instanceof AST_Call && parent.expression === node) return;
1466 } while (parent instanceof AST_PropAccess && parent.expression === node);
1467 output.print(typeof self.pure == "string" ? "/*" + self.pure + "*/" : "/*@__PURE__*/");
1468 }
1469 function print_call_args(self, output) {
1470 if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
1471 output.add_mapping(self.start);
1472 }
1473 output.with_parens(function() {
1474 self.args.forEach(function(expr, i) {
1475 if (i) output.comma();
1476 expr.print(output);
1477 });
1478 });
1479 }
1480 DEFPRINT(AST_Call, function(output) {
1481 var self = this;
1482 print_annotation(self, output);
1483 self.expression.print(output);
1484 if (self.optional) output.print("?.");
1485 print_call_args(self, output);
1486 });
1487 DEFPRINT(AST_New, function(output) {
1488 var self = this;
1489 print_annotation(self, output);
1490 output.print("new");
1491 output.space();
1492 self.expression.print(output);
1493 if (need_constructor_parens(self, output)) print_call_args(self, output);
1494 });
1495 DEFPRINT(AST_Sequence, function(output) {
1496 this.expressions.forEach(function(node, index) {
1497 if (index > 0) {
1498 output.comma();
1499 if (output.should_break()) {
1500 output.newline();
1501 output.indent();
1502 }
1503 }
1504 node.print(output);
1505 });
1506 });
1507 DEFPRINT(AST_Dot, function(output) {
1508 var self = this;
1509 var expr = self.expression;
1510 expr.print(output);
1511 var prop = self.property;
1512 if (output.option("ie") && RESERVED_WORDS[prop]) {
1513 output.print(self.optional ? "?.[" : "[");
1514 output.add_mapping(self.end);
1515 output.print_string(prop);
1516 output.print("]");
1517 } else {
1518 if (expr instanceof AST_Number && !/[ex.)]/i.test(output.last())) output.print(".");
1519 output.print(self.optional ? "?." : ".");
1520 // the name after dot would be mapped about here.
1521 output.add_mapping(self.end);
1522 output.print_name(prop);
1523 }
1524 });
1525 DEFPRINT(AST_Sub, function(output) {
1526 var self = this;
1527 self.expression.print(output);
1528 output.print(self.optional ? "?.[" : "[");
1529 self.property.print(output);
1530 output.print("]");
1531 });
1532 DEFPRINT(AST_Spread, function(output) {
1533 output.print("...");
1534 this.expression.print(output);
1535 });
1536 DEFPRINT(AST_UnaryPrefix, function(output) {
1537 var op = this.operator;
1538 var exp = this.expression;
1539 output.print(op);
1540 if (/^[a-z]/i.test(op)
1541 || (/[+-]$/.test(op)
1542 && exp instanceof AST_UnaryPrefix
1543 && /^[+-]/.test(exp.operator))) {
1544 output.space();
1545 }
1546 exp.print(output);
1547 });
1548 DEFPRINT(AST_UnaryPostfix, function(output) {
1549 this.expression.print(output);
1550 output.print(this.operator);
1551 });
1552 DEFPRINT(AST_Binary, function(output) {
1553 var self = this;
1554 self.left.print(output);
1555 output.space();
1556 output.print(self.operator);
1557 output.space();
1558 self.right.print(output);
1559 });
1560 DEFPRINT(AST_Conditional, function(output) {
1561 var self = this;
1562 self.condition.print(output);
1563 output.space();
1564 output.print("?");
1565 output.space();
1566 self.consequent.print(output);
1567 output.space();
1568 output.colon();
1569 self.alternative.print(output);
1570 });
1571 DEFPRINT(AST_Await, function(output) {
1572 output.print("await");
1573 output.space();
1574 this.expression.print(output);
1575 });
1576 DEFPRINT(AST_Yield, function(output) {
1577 output.print(this.nested ? "yield*" : "yield");
1578 if (this.expression) {
1579 output.space();
1580 this.expression.print(output);
1581 }
1582 });
1583
1584 /* -----[ literals ]----- */
1585 DEFPRINT(AST_Array, function(output) {
1586 var a = this.elements, len = a.length;
1587 output.with_square(len > 0 ? function() {
1588 output.space();
1589 a.forEach(function(exp, i) {
1590 if (i) output.comma();
1591 exp.print(output);
1592 // If the final element is a hole, we need to make sure it
1593 // doesn't look like a trailing comma, by inserting an actual
1594 // trailing comma.
1595 if (i === len - 1 && exp instanceof AST_Hole)
1596 output.comma();
1597 });
1598 output.space();
1599 } : noop);
1600 });
1601 DEFPRINT(AST_DestructuredArray, function(output) {
1602 var a = this.elements, len = a.length, rest = this.rest;
1603 output.with_square(len || rest ? function() {
1604 output.space();
1605 a.forEach(function(exp, i) {
1606 if (i) output.comma();
1607 exp.print(output);
1608 });
1609 if (rest) {
1610 if (len) output.comma();
1611 output.print("...");
1612 rest.print(output);
1613 } else if (a[len - 1] instanceof AST_Hole) {
1614 // If the final element is a hole, we need to make sure it
1615 // doesn't look like a trailing comma, by inserting an actual
1616 // trailing comma.
1617 output.comma();
1618 }
1619 output.space();
1620 } : noop);
1621 });
1622 DEFPRINT(AST_DestructuredKeyVal, function(output) {
1623 var self = this;
1624 var key = print_property_key(self, output);
1625 var value = self.value;
1626 if (key) {
1627 if (value instanceof AST_DefaultValue) {
1628 if (value.name instanceof AST_Symbol && key == get_symbol_name(value.name)) {
1629 output.space();
1630 output.print("=");
1631 output.space();
1632 value.value.print(output);
1633 return;
1634 }
1635 } else if (value instanceof AST_Symbol) {
1636 if (key == get_symbol_name(value)) return;
1637 }
1638 }
1639 output.colon();
1640 value.print(output);
1641 });
1642 DEFPRINT(AST_DestructuredObject, function(output) {
1643 var props = this.properties, len = props.length, rest = this.rest;
1644 if (len || rest) output.with_block(function() {
1645 props.forEach(function(prop, i) {
1646 if (i) {
1647 output.print(",");
1648 output.newline();
1649 }
1650 output.indent();
1651 prop.print(output);
1652 });
1653 if (rest) {
1654 if (len) {
1655 output.print(",");
1656 output.newline();
1657 }
1658 output.indent();
1659 output.print("...");
1660 rest.print(output);
1661 }
1662 output.newline();
1663 });
1664 else print_braced_empty(this, output);
1665 });
1666 function print_properties(self, output, no_comma) {
1667 var props = self.properties;
1668 if (props.length > 0) output.with_block(function() {
1669 props.forEach(function(prop, i) {
1670 if (i) {
1671 if (!no_comma) output.print(",");
1672 output.newline();
1673 }
1674 output.indent();
1675 prop.print(output);
1676 });
1677 output.newline();
1678 });
1679 else print_braced_empty(self, output);
1680 }
1681 DEFPRINT(AST_Object, function(output) {
1682 print_properties(this, output);
1683 });
1684
1685 function print_property_key(self, output) {
1686 var key = self.key;
1687 if (key instanceof AST_Node) return output.with_square(function() {
1688 key.print(output);
1689 });
1690 var quote = self.start && self.start.quote;
1691 if (output.option("quote_keys") || quote && output.option("keep_quoted_props")) {
1692 output.print_string(key, quote);
1693 } else if ("" + +key == key && key >= 0) {
1694 output.print(make_num(key));
1695 } else if (self.private) {
1696 output.print_name(key);
1697 } else if (RESERVED_WORDS[key] ? !output.option("ie") : is_identifier_string(key)) {
1698 output.print_name(key);
1699 return key;
1700 } else {
1701 output.print_string(key, quote);
1702 }
1703 }
1704 DEFPRINT(AST_ObjectKeyVal, function(output) {
1705 var self = this;
1706 print_property_key(self, output);
1707 output.colon();
1708 self.value.print(output);
1709 });
1710 DEFPRINT(AST_ObjectMethod, function(output) {
1711 print_method(this, output);
1712 });
1713 function print_accessor(type) {
1714 return function(output) {
1715 var self = this;
1716 if (self.static) {
1717 output.print("static");
1718 output.space();
1719 }
1720 output.print(type);
1721 output.space();
1722 print_property_key(self, output);
1723 print_lambda(self.value, output);
1724 };
1725 }
1726 DEFPRINT(AST_ObjectGetter, print_accessor("get"));
1727 DEFPRINT(AST_ObjectSetter, print_accessor("set"));
1728 function get_symbol_name(sym) {
1729 var def = sym.definition();
1730 return def && def.mangled_name || sym.name;
1731 }
1732 DEFPRINT(AST_Symbol, function(output) {
1733 output.print_name(get_symbol_name(this));
1734 });
1735 DEFPRINT(AST_SymbolExport, function(output) {
1736 var self = this;
1737 var name = get_symbol_name(self);
1738 output.print_name(name);
1739 var alias = self.alias;
1740 if (alias != name) {
1741 output.space();
1742 output.print("as");
1743 output.space();
1744 output.print_name(alias);
1745 }
1746 });
1747 DEFPRINT(AST_SymbolImport, function(output) {
1748 var self = this;
1749 var name = get_symbol_name(self);
1750 var key = self.key;
1751 if (key && key != name) {
1752 output.print_name(key);
1753 output.space();
1754 output.print("as");
1755 output.space();
1756 }
1757 output.print_name(name);
1758 });
1759 DEFPRINT(AST_Hole, noop);
1760 DEFPRINT(AST_Template, function(output) {
1761 var self = this;
1762 if (self.tag) self.tag.print(output);
1763 output.print("`");
1764 for (var i = 0; i < self.expressions.length; i++) {
1765 output.print(self.strings[i]);
1766 output.print("${");
1767 self.expressions[i].print(output);
1768 output.print("}");
1769 }
1770 output.print(self.strings[i]);
1771 output.print("`");
1772 });
1773 DEFPRINT(AST_Constant, function(output) {
1774 output.print("" + this.value);
1775 });
1776 DEFPRINT(AST_String, function(output) {
1777 output.print_string(this.value, this.quote);
1778 });
1779 DEFPRINT(AST_Number, function(output) {
1780 var start = this.start;
1781 if (use_asm && start && start.raw != null) {
1782 output.print(start.raw);
1783 } else {
1784 output.print(make_num(this.value));
1785 }
1786 });
1787
1788 DEFPRINT(AST_RegExp, function(output) {
1789 var regexp = this.value;
1790 var str = regexp.toString();
1791 var end = str.lastIndexOf("/");
1792 if (regexp.raw_source) {
1793 str = "/" + regexp.raw_source + str.slice(end);
1794 } else if (end == 1) {
1795 str = "/(?:)" + str.slice(end);
1796 } else if (str.indexOf("/", 1) < end) {
1797 str = "/" + str.slice(1, end).replace(/\\\\|[^/]?\//g, function(match) {
1798 return match[0] == "\\" ? match : match.slice(0, -1) + "\\/";
1799 }) + str.slice(end);
1800 }
1801 output.print(output.to_utf8(str).replace(/\\(?:\0(?![0-9])|[^\0])/g, function(match) {
1802 switch (match[1]) {
1803 case "\n": return "\\n";
1804 case "\r": return "\\r";
1805 case "\t": return "\t";
1806 case "\b": return "\b";
1807 case "\f": return "\f";
1808 case "\0": return "\0";
1809 case "\x0B": return "\v";
1810 case "\u2028": return "\\u2028";
1811 case "\u2029": return "\\u2029";
1812 default: return match;
1813 }
1814 }).replace(/[\n\r\u2028\u2029]/g, function(c) {
1815 switch (c) {
1816 case "\n": return "\\n";
1817 case "\r": return "\\r";
1818 case "\u2028": return "\\u2028";
1819 case "\u2029": return "\\u2029";
1820 }
1821 }));
1822 var p = output.parent();
1823 if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === this)
1824 output.print(" ");
1825 });
1826
1827 function force_statement(stat, output) {
1828 if (output.option("braces") && !(stat instanceof AST_Const || stat instanceof AST_Let)) {
1829 make_block(stat, output);
1830 } else if (stat instanceof AST_EmptyStatement) {
1831 output.force_semicolon();
1832 } else {
1833 output.space();
1834 stat.print(output);
1835 }
1836 }
1837
1838 // self should be AST_New. decide if we want to show parens or not.
1839 function need_constructor_parens(self, output) {
1840 // Always print parentheses with arguments
1841 if (self.args.length > 0) return true;
1842
1843 return output.option("beautify");
1844 }
1845
1846 function best_of(a) {
1847 var best = a[0], len = best.length;
1848 for (var i = 1; i < a.length; ++i) {
1849 if (a[i].length < len) {
1850 best = a[i];
1851 len = best.length;
1852 }
1853 }
1854 return best;
1855 }
1856
1857 function make_num(num) {
1858 var str = num.toString(10).replace(/^0\./, ".").replace("e+", "e");
1859 var candidates = [ str ];
1860 if (Math.floor(num) === num) {
1861 if (num < 0) {
1862 candidates.push("-0x" + (-num).toString(16).toLowerCase());
1863 } else {
1864 candidates.push("0x" + num.toString(16).toLowerCase());
1865 }
1866 }
1867 var match, len, digits;
1868 if (match = /^\.0+/.exec(str)) {
1869 len = match[0].length;
1870 digits = str.slice(len);
1871 candidates.push(digits + "e-" + (digits.length + len - 1));
1872 } else if (match = /0+$/.exec(str)) {
1873 len = match[0].length;
1874 candidates.push(str.slice(0, -len) + "e" + len);
1875 } else if (match = /^(\d)\.(\d+)e(-?\d+)$/.exec(str)) {
1876 candidates.push(match[1] + match[2] + "e" + (match[3] - match[2].length));
1877 }
1878 return best_of(candidates);
1879 }
1880
1881 function make_block(stmt, output) {
1882 output.space();
1883 if (stmt instanceof AST_EmptyStatement) {
1884 print_braced_empty(stmt, output);
1885 } else if (stmt instanceof AST_BlockStatement) {
1886 stmt.print(output);
1887 } else output.with_block(function() {
1888 output.indent();
1889 stmt.print(output);
1890 output.newline();
1891 });
1892 }
1893
1894 /* -----[ source map generators ]----- */
1895
1896 function DEFMAP(nodetype, generator) {
1897 nodetype.forEach(function(nodetype) {
1898 nodetype.DEFMETHOD("add_source_map", generator);
1899 });
1900 }
1901
1902 DEFMAP([
1903 // We could easily add info for ALL nodes, but it seems to me that
1904 // would be quite wasteful, hence this noop in the base class.
1905 AST_Node,
1906 // since the label symbol will mark it
1907 AST_LabeledStatement,
1908 ], noop);
1909
1910 // XXX: I'm not exactly sure if we need it for all of these nodes,
1911 // or if we should add even more.
1912 DEFMAP([
1913 AST_Array,
1914 AST_BlockStatement,
1915 AST_Catch,
1916 AST_Constant,
1917 AST_Debugger,
1918 AST_Definitions,
1919 AST_Destructured,
1920 AST_Finally,
1921 AST_Jump,
1922 AST_Lambda,
1923 AST_New,
1924 AST_Object,
1925 AST_StatementWithBody,
1926 AST_Symbol,
1927 AST_Switch,
1928 AST_SwitchBranch,
1929 AST_Try,
1930 ], function(output) {
1931 output.add_mapping(this.start);
1932 });
1933
1934 DEFMAP([
1935 AST_ClassProperty,
1936 AST_DestructuredKeyVal,
1937 AST_ObjectProperty,
1938 ], function(output) {
1939 if (typeof this.key == "string") output.add_mapping(this.start, this.key);
1940 });
1941})();