UNPKG

73.3 kBJavaScriptView Raw
1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
5
6 -------------------------------- (C) ---------------------------------
7
8 Author: Mihai Bazon
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
11
12 Distributed under the BSD license:
13
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
18 are met:
19
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
22 disclaimer.
23
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
28
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 SUCH DAMAGE.
41
42 ***********************************************************************/
43
44"use strict";
45
46import {
47 defaults,
48 makePredicate,
49 noop,
50 regexp_source_fix,
51 sort_regexp_flags,
52 return_false,
53 return_true,
54} from "./utils/index.js";
55import { first_in_statement, left_is_object } from "./utils/first_in_statement.js";
56import {
57 AST_Array,
58 AST_Arrow,
59 AST_Assign,
60 AST_Await,
61 AST_BigInt,
62 AST_Binary,
63 AST_BlockStatement,
64 AST_Break,
65 AST_Call,
66 AST_Case,
67 AST_Catch,
68 AST_Chain,
69 AST_Class,
70 AST_ClassExpression,
71 AST_ClassProperty,
72 AST_ConciseMethod,
73 AST_Conditional,
74 AST_Const,
75 AST_Constant,
76 AST_Continue,
77 AST_Debugger,
78 AST_Default,
79 AST_DefaultAssign,
80 AST_Definitions,
81 AST_Defun,
82 AST_Destructuring,
83 AST_Directive,
84 AST_Do,
85 AST_Dot,
86 AST_EmptyStatement,
87 AST_Exit,
88 AST_Expansion,
89 AST_Export,
90 AST_Finally,
91 AST_For,
92 AST_ForIn,
93 AST_ForOf,
94 AST_Function,
95 AST_Hole,
96 AST_If,
97 AST_Import,
98 AST_ImportMeta,
99 AST_Jump,
100 AST_LabeledStatement,
101 AST_Lambda,
102 AST_Let,
103 AST_LoopControl,
104 AST_NameMapping,
105 AST_New,
106 AST_NewTarget,
107 AST_Node,
108 AST_Number,
109 AST_Object,
110 AST_ObjectGetter,
111 AST_ObjectKeyVal,
112 AST_ObjectProperty,
113 AST_ObjectSetter,
114 AST_PrefixedTemplateString,
115 AST_PropAccess,
116 AST_RegExp,
117 AST_Return,
118 AST_Scope,
119 AST_Sequence,
120 AST_SimpleStatement,
121 AST_Statement,
122 AST_StatementWithBody,
123 AST_String,
124 AST_Sub,
125 AST_Super,
126 AST_Switch,
127 AST_SwitchBranch,
128 AST_Symbol,
129 AST_SymbolClassProperty,
130 AST_SymbolMethod,
131 AST_SymbolRef,
132 AST_TemplateSegment,
133 AST_TemplateString,
134 AST_This,
135 AST_Throw,
136 AST_Toplevel,
137 AST_Try,
138 AST_Unary,
139 AST_UnaryPostfix,
140 AST_UnaryPrefix,
141 AST_Var,
142 AST_VarDef,
143 AST_While,
144 AST_With,
145 AST_Yield,
146 TreeWalker,
147 walk,
148 walk_abort
149} from "./ast.js";
150import {
151 get_full_char_code,
152 get_full_char,
153 is_identifier_char,
154 is_basic_identifier_string,
155 is_identifier_string,
156 PRECEDENCE,
157 RESERVED_WORDS,
158} from "./parse.js";
159
160const EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
161const CODE_LINE_BREAK = 10;
162const CODE_SPACE = 32;
163
164const r_annotation = /[@#]__(PURE|INLINE|NOINLINE)__/g;
165
166function is_some_comments(comment) {
167 // multiline comment
168 return (
169 (comment.type === "comment2" || comment.type === "comment1")
170 && /@preserve|@lic|@cc_on|^\**!/i.test(comment.value)
171 );
172}
173
174function OutputStream(options) {
175
176 var readonly = !options;
177 options = defaults(options, {
178 ascii_only : false,
179 beautify : false,
180 braces : false,
181 comments : "some",
182 ecma : 5,
183 ie8 : false,
184 indent_level : 4,
185 indent_start : 0,
186 inline_script : true,
187 keep_numbers : false,
188 keep_quoted_props : false,
189 max_line_len : false,
190 preamble : null,
191 preserve_annotations : false,
192 quote_keys : false,
193 quote_style : 0,
194 safari10 : false,
195 semicolons : true,
196 shebang : true,
197 shorthand : undefined,
198 source_map : null,
199 webkit : false,
200 width : 80,
201 wrap_iife : false,
202 wrap_func_args : true,
203 }, true);
204
205 if (options.shorthand === undefined)
206 options.shorthand = options.ecma > 5;
207
208 // Convert comment option to RegExp if neccessary and set up comments filter
209 var comment_filter = return_false; // Default case, throw all comments away
210 if (options.comments) {
211 let comments = options.comments;
212 if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) {
213 var regex_pos = options.comments.lastIndexOf("/");
214 comments = new RegExp(
215 options.comments.substr(1, regex_pos - 1),
216 options.comments.substr(regex_pos + 1)
217 );
218 }
219 if (comments instanceof RegExp) {
220 comment_filter = function(comment) {
221 return comment.type != "comment5" && comments.test(comment.value);
222 };
223 } else if (typeof comments === "function") {
224 comment_filter = function(comment) {
225 return comment.type != "comment5" && comments(this, comment);
226 };
227 } else if (comments === "some") {
228 comment_filter = is_some_comments;
229 } else { // NOTE includes "all" option
230 comment_filter = return_true;
231 }
232 }
233
234 var indentation = 0;
235 var current_col = 0;
236 var current_line = 1;
237 var current_pos = 0;
238 var OUTPUT = "";
239 let printed_comments = new Set();
240
241 var to_utf8 = options.ascii_only ? function(str, identifier) {
242 if (options.ecma >= 2015) {
243 str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) {
244 var code = get_full_char_code(ch, 0).toString(16);
245 return "\\u{" + code + "}";
246 });
247 }
248 return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
249 var code = ch.charCodeAt(0).toString(16);
250 if (code.length <= 2 && !identifier) {
251 while (code.length < 2) code = "0" + code;
252 return "\\x" + code;
253 } else {
254 while (code.length < 4) code = "0" + code;
255 return "\\u" + code;
256 }
257 });
258 } : function(str) {
259 return str.replace(/[\ud800-\udbff][\udc00-\udfff]|([\ud800-\udbff]|[\udc00-\udfff])/g, function(match, lone) {
260 if (lone) {
261 return "\\u" + lone.charCodeAt(0).toString(16);
262 }
263 return match;
264 });
265 };
266
267 function make_string(str, quote) {
268 var dq = 0, sq = 0;
269 str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g,
270 function(s, i) {
271 switch (s) {
272 case '"': ++dq; return '"';
273 case "'": ++sq; return "'";
274 case "\\": return "\\\\";
275 case "\n": return "\\n";
276 case "\r": return "\\r";
277 case "\t": return "\\t";
278 case "\b": return "\\b";
279 case "\f": return "\\f";
280 case "\x0B": return options.ie8 ? "\\x0B" : "\\v";
281 case "\u2028": return "\\u2028";
282 case "\u2029": return "\\u2029";
283 case "\ufeff": return "\\ufeff";
284 case "\0":
285 return /[0-9]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
286 }
287 return s;
288 });
289 function quote_single() {
290 return "'" + str.replace(/\x27/g, "\\'") + "'";
291 }
292 function quote_double() {
293 return '"' + str.replace(/\x22/g, '\\"') + '"';
294 }
295 function quote_template() {
296 return "`" + str.replace(/`/g, "\\`") + "`";
297 }
298 str = to_utf8(str);
299 if (quote === "`") return quote_template();
300 switch (options.quote_style) {
301 case 1:
302 return quote_single();
303 case 2:
304 return quote_double();
305 case 3:
306 return quote == "'" ? quote_single() : quote_double();
307 default:
308 return dq > sq ? quote_single() : quote_double();
309 }
310 }
311
312 function encode_string(str, quote) {
313 var ret = make_string(str, quote);
314 if (options.inline_script) {
315 ret = ret.replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2");
316 ret = ret.replace(/\x3c!--/g, "\\x3c!--");
317 ret = ret.replace(/--\x3e/g, "--\\x3e");
318 }
319 return ret;
320 }
321
322 function make_name(name) {
323 name = name.toString();
324 name = to_utf8(name, true);
325 return name;
326 }
327
328 function make_indent(back) {
329 return " ".repeat(options.indent_start + indentation - back * options.indent_level);
330 }
331
332 /* -----[ beautification/minification ]----- */
333
334 var has_parens = false;
335 var might_need_space = false;
336 var might_need_semicolon = false;
337 var might_add_newline = 0;
338 var need_newline_indented = false;
339 var need_space = false;
340 var newline_insert = -1;
341 var last = "";
342 var mapping_token, mapping_name, mappings = options.source_map && [];
343
344 var do_add_mapping = mappings ? function() {
345 mappings.forEach(function(mapping) {
346 try {
347 options.source_map.add(
348 mapping.token.file,
349 mapping.line, mapping.col,
350 mapping.token.line, mapping.token.col,
351 !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
352 );
353 } catch(ex) {
354 // Ignore bad mapping
355 }
356 });
357 mappings = [];
358 } : noop;
359
360 var ensure_line_len = options.max_line_len ? function() {
361 if (current_col > options.max_line_len) {
362 if (might_add_newline) {
363 var left = OUTPUT.slice(0, might_add_newline);
364 var right = OUTPUT.slice(might_add_newline);
365 if (mappings) {
366 var delta = right.length - current_col;
367 mappings.forEach(function(mapping) {
368 mapping.line++;
369 mapping.col += delta;
370 });
371 }
372 OUTPUT = left + "\n" + right;
373 current_line++;
374 current_pos++;
375 current_col = right.length;
376 }
377 }
378 if (might_add_newline) {
379 might_add_newline = 0;
380 do_add_mapping();
381 }
382 } : noop;
383
384 var requireSemicolonChars = makePredicate("( [ + * / - , . `");
385
386 function print(str) {
387 str = String(str);
388 var ch = get_full_char(str, 0);
389 if (need_newline_indented && ch) {
390 need_newline_indented = false;
391 if (ch !== "\n") {
392 print("\n");
393 indent();
394 }
395 }
396 if (need_space && ch) {
397 need_space = false;
398 if (!/[\s;})]/.test(ch)) {
399 space();
400 }
401 }
402 newline_insert = -1;
403 var prev = last.charAt(last.length - 1);
404 if (might_need_semicolon) {
405 might_need_semicolon = false;
406
407 if (prev === ":" && ch === "}" || (!ch || !";}".includes(ch)) && prev !== ";") {
408 if (options.semicolons || requireSemicolonChars.has(ch)) {
409 OUTPUT += ";";
410 current_col++;
411 current_pos++;
412 } else {
413 ensure_line_len();
414 if (current_col > 0) {
415 OUTPUT += "\n";
416 current_pos++;
417 current_line++;
418 current_col = 0;
419 }
420
421 if (/^\s+$/.test(str)) {
422 // reset the semicolon flag, since we didn't print one
423 // now and might still have to later
424 might_need_semicolon = true;
425 }
426 }
427
428 if (!options.beautify)
429 might_need_space = false;
430 }
431 }
432
433 if (might_need_space) {
434 if ((is_identifier_char(prev)
435 && (is_identifier_char(ch) || ch == "\\"))
436 || (ch == "/" && ch == prev)
437 || ((ch == "+" || ch == "-") && ch == last)
438 ) {
439 OUTPUT += " ";
440 current_col++;
441 current_pos++;
442 }
443 might_need_space = false;
444 }
445
446 if (mapping_token) {
447 mappings.push({
448 token: mapping_token,
449 name: mapping_name,
450 line: current_line,
451 col: current_col
452 });
453 mapping_token = false;
454 if (!might_add_newline) do_add_mapping();
455 }
456
457 OUTPUT += str;
458 has_parens = str[str.length - 1] == "(";
459 current_pos += str.length;
460 var a = str.split(/\r?\n/), n = a.length - 1;
461 current_line += n;
462 current_col += a[0].length;
463 if (n > 0) {
464 ensure_line_len();
465 current_col = a[n].length;
466 }
467 last = str;
468 }
469
470 var star = function() {
471 print("*");
472 };
473
474 var space = options.beautify ? function() {
475 print(" ");
476 } : function() {
477 might_need_space = true;
478 };
479
480 var indent = options.beautify ? function(half) {
481 if (options.beautify) {
482 print(make_indent(half ? 0.5 : 0));
483 }
484 } : noop;
485
486 var with_indent = options.beautify ? function(col, cont) {
487 if (col === true) col = next_indent();
488 var save_indentation = indentation;
489 indentation = col;
490 var ret = cont();
491 indentation = save_indentation;
492 return ret;
493 } : function(col, cont) { return cont(); };
494
495 var newline = options.beautify ? function() {
496 if (newline_insert < 0) return print("\n");
497 if (OUTPUT[newline_insert] != "\n") {
498 OUTPUT = OUTPUT.slice(0, newline_insert) + "\n" + OUTPUT.slice(newline_insert);
499 current_pos++;
500 current_line++;
501 }
502 newline_insert++;
503 } : options.max_line_len ? function() {
504 ensure_line_len();
505 might_add_newline = OUTPUT.length;
506 } : noop;
507
508 var semicolon = options.beautify ? function() {
509 print(";");
510 } : function() {
511 might_need_semicolon = true;
512 };
513
514 function force_semicolon() {
515 might_need_semicolon = false;
516 print(";");
517 }
518
519 function next_indent() {
520 return indentation + options.indent_level;
521 }
522
523 function with_block(cont) {
524 var ret;
525 print("{");
526 newline();
527 with_indent(next_indent(), function() {
528 ret = cont();
529 });
530 indent();
531 print("}");
532 return ret;
533 }
534
535 function with_parens(cont) {
536 print("(");
537 //XXX: still nice to have that for argument lists
538 //var ret = with_indent(current_col, cont);
539 var ret = cont();
540 print(")");
541 return ret;
542 }
543
544 function with_square(cont) {
545 print("[");
546 //var ret = with_indent(current_col, cont);
547 var ret = cont();
548 print("]");
549 return ret;
550 }
551
552 function comma() {
553 print(",");
554 space();
555 }
556
557 function colon() {
558 print(":");
559 space();
560 }
561
562 var add_mapping = mappings ? function(token, name) {
563 mapping_token = token;
564 mapping_name = name;
565 } : noop;
566
567 function get() {
568 if (might_add_newline) {
569 ensure_line_len();
570 }
571 return OUTPUT;
572 }
573
574 function has_nlb() {
575 let n = OUTPUT.length - 1;
576 while (n >= 0) {
577 const code = OUTPUT.charCodeAt(n);
578 if (code === CODE_LINE_BREAK) {
579 return true;
580 }
581
582 if (code !== CODE_SPACE) {
583 return false;
584 }
585 n--;
586 }
587 return true;
588 }
589
590 function filter_comment(comment) {
591 if (!options.preserve_annotations) {
592 comment = comment.replace(r_annotation, " ");
593 }
594 if (/^\s*$/.test(comment)) {
595 return "";
596 }
597 return comment.replace(/(<\s*\/\s*)(script)/i, "<\\/$2");
598 }
599
600 function prepend_comments(node) {
601 var self = this;
602 var start = node.start;
603 if (!start) return;
604 var printed_comments = self.printed_comments;
605
606 // There cannot be a newline between return and its value.
607 const return_with_value = node instanceof AST_Exit && node.value;
608
609 if (
610 start.comments_before
611 && printed_comments.has(start.comments_before)
612 ) {
613 if (return_with_value) {
614 start.comments_before = [];
615 } else {
616 return;
617 }
618 }
619
620 var comments = start.comments_before;
621 if (!comments) {
622 comments = start.comments_before = [];
623 }
624 printed_comments.add(comments);
625
626 if (return_with_value) {
627 var tw = new TreeWalker(function(node) {
628 var parent = tw.parent();
629 if (parent instanceof AST_Exit
630 || parent instanceof AST_Binary && parent.left === node
631 || parent.TYPE == "Call" && parent.expression === node
632 || parent instanceof AST_Conditional && parent.condition === node
633 || parent instanceof AST_Dot && parent.expression === node
634 || parent instanceof AST_Sequence && parent.expressions[0] === node
635 || parent instanceof AST_Sub && parent.expression === node
636 || parent instanceof AST_UnaryPostfix) {
637 if (!node.start) return;
638 var text = node.start.comments_before;
639 if (text && !printed_comments.has(text)) {
640 printed_comments.add(text);
641 comments = comments.concat(text);
642 }
643 } else {
644 return true;
645 }
646 });
647 tw.push(node);
648 node.value.walk(tw);
649 }
650
651 if (current_pos == 0) {
652 if (comments.length > 0 && options.shebang && comments[0].type === "comment5"
653 && !printed_comments.has(comments[0])) {
654 print("#!" + comments.shift().value + "\n");
655 indent();
656 }
657 var preamble = options.preamble;
658 if (preamble) {
659 print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
660 }
661 }
662
663 comments = comments.filter(comment_filter, node).filter(c => !printed_comments.has(c));
664 if (comments.length == 0) return;
665 var last_nlb = has_nlb();
666 comments.forEach(function(c, i) {
667 printed_comments.add(c);
668 if (!last_nlb) {
669 if (c.nlb) {
670 print("\n");
671 indent();
672 last_nlb = true;
673 } else if (i > 0) {
674 space();
675 }
676 }
677
678 if (/comment[134]/.test(c.type)) {
679 var value = filter_comment(c.value);
680 if (value) {
681 print("//" + value + "\n");
682 indent();
683 }
684 last_nlb = true;
685 } else if (c.type == "comment2") {
686 var value = filter_comment(c.value);
687 if (value) {
688 print("/*" + value + "*/");
689 }
690 last_nlb = false;
691 }
692 });
693 if (!last_nlb) {
694 if (start.nlb) {
695 print("\n");
696 indent();
697 } else {
698 space();
699 }
700 }
701 }
702
703 function append_comments(node, tail) {
704 var self = this;
705 var token = node.end;
706 if (!token) return;
707 var printed_comments = self.printed_comments;
708 var comments = token[tail ? "comments_before" : "comments_after"];
709 if (!comments || printed_comments.has(comments)) return;
710 if (!(node instanceof AST_Statement || comments.every((c) =>
711 !/comment[134]/.test(c.type)
712 ))) return;
713 printed_comments.add(comments);
714 var insert = OUTPUT.length;
715 comments.filter(comment_filter, node).forEach(function(c, i) {
716 if (printed_comments.has(c)) return;
717 printed_comments.add(c);
718 need_space = false;
719 if (need_newline_indented) {
720 print("\n");
721 indent();
722 need_newline_indented = false;
723 } else if (c.nlb && (i > 0 || !has_nlb())) {
724 print("\n");
725 indent();
726 } else if (i > 0 || !tail) {
727 space();
728 }
729 if (/comment[134]/.test(c.type)) {
730 const value = filter_comment(c.value);
731 if (value) {
732 print("//" + value);
733 }
734 need_newline_indented = true;
735 } else if (c.type == "comment2") {
736 const value = filter_comment(c.value);
737 if (value) {
738 print("/*" + value + "*/");
739 }
740 need_space = true;
741 }
742 });
743 if (OUTPUT.length > insert) newline_insert = insert;
744 }
745
746 var stack = [];
747 return {
748 get : get,
749 toString : get,
750 indent : indent,
751 in_directive : false,
752 use_asm : null,
753 active_scope : null,
754 indentation : function() { return indentation; },
755 current_width : function() { return current_col - indentation; },
756 should_break : function() { return options.width && this.current_width() >= options.width; },
757 has_parens : function() { return has_parens; },
758 newline : newline,
759 print : print,
760 star : star,
761 space : space,
762 comma : comma,
763 colon : colon,
764 last : function() { return last; },
765 semicolon : semicolon,
766 force_semicolon : force_semicolon,
767 to_utf8 : to_utf8,
768 print_name : function(name) { print(make_name(name)); },
769 print_string : function(str, quote, escape_directive) {
770 var encoded = encode_string(str, quote);
771 if (escape_directive === true && !encoded.includes("\\")) {
772 // Insert semicolons to break directive prologue
773 if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
774 force_semicolon();
775 }
776 force_semicolon();
777 }
778 print(encoded);
779 },
780 print_template_string_chars: function(str) {
781 var encoded = encode_string(str, "`").replace(/\${/g, "\\${");
782 return print(encoded.substr(1, encoded.length - 2));
783 },
784 encode_string : encode_string,
785 next_indent : next_indent,
786 with_indent : with_indent,
787 with_block : with_block,
788 with_parens : with_parens,
789 with_square : with_square,
790 add_mapping : add_mapping,
791 option : function(opt) { return options[opt]; },
792 printed_comments: printed_comments,
793 prepend_comments: readonly ? noop : prepend_comments,
794 append_comments : readonly || comment_filter === return_false ? noop : append_comments,
795 line : function() { return current_line; },
796 col : function() { return current_col; },
797 pos : function() { return current_pos; },
798 push_node : function(node) { stack.push(node); },
799 pop_node : function() { return stack.pop(); },
800 parent : function(n) {
801 return stack[stack.length - 2 - (n || 0)];
802 }
803 };
804
805}
806
807/* -----[ code generators ]----- */
808
809(function() {
810
811 /* -----[ utils ]----- */
812
813 function DEFPRINT(nodetype, generator) {
814 nodetype.DEFMETHOD("_codegen", generator);
815 }
816
817 AST_Node.DEFMETHOD("print", function(output, force_parens) {
818 var self = this, generator = self._codegen;
819 if (self instanceof AST_Scope) {
820 output.active_scope = self;
821 } else if (!output.use_asm && self instanceof AST_Directive && self.value == "use asm") {
822 output.use_asm = output.active_scope;
823 }
824 function doit() {
825 output.prepend_comments(self);
826 self.add_source_map(output);
827 generator(self, output);
828 output.append_comments(self);
829 }
830 output.push_node(self);
831 if (force_parens || self.needs_parens(output)) {
832 output.with_parens(doit);
833 } else {
834 doit();
835 }
836 output.pop_node();
837 if (self === output.use_asm) {
838 output.use_asm = null;
839 }
840 });
841 AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
842
843 AST_Node.DEFMETHOD("print_to_string", function(options) {
844 var output = OutputStream(options);
845 this.print(output);
846 return output.get();
847 });
848
849 /* -----[ PARENTHESES ]----- */
850
851 function PARENS(nodetype, func) {
852 if (Array.isArray(nodetype)) {
853 nodetype.forEach(function(nodetype) {
854 PARENS(nodetype, func);
855 });
856 } else {
857 nodetype.DEFMETHOD("needs_parens", func);
858 }
859 }
860
861 PARENS(AST_Node, return_false);
862
863 // a function expression needs parens around it when it's provably
864 // the first token to appear in a statement.
865 PARENS(AST_Function, function(output) {
866 if (!output.has_parens() && first_in_statement(output)) {
867 return true;
868 }
869
870 if (output.option("webkit")) {
871 var p = output.parent();
872 if (p instanceof AST_PropAccess && p.expression === this) {
873 return true;
874 }
875 }
876
877 if (output.option("wrap_iife")) {
878 var p = output.parent();
879 if (p instanceof AST_Call && p.expression === this) {
880 return true;
881 }
882 }
883
884 if (output.option("wrap_func_args")) {
885 var p = output.parent();
886 if (p instanceof AST_Call && p.args.includes(this)) {
887 return true;
888 }
889 }
890
891 return false;
892 });
893
894 PARENS(AST_Arrow, function(output) {
895 var p = output.parent();
896
897 if (
898 output.option("wrap_func_args")
899 && p instanceof AST_Call
900 && p.args.includes(this)
901 ) {
902 return true;
903 }
904 return p instanceof AST_PropAccess && p.expression === this;
905 });
906
907 // same goes for an object literal (as in AST_Function), because
908 // otherwise {...} would be interpreted as a block of code.
909 PARENS(AST_Object, function(output) {
910 return !output.has_parens() && first_in_statement(output);
911 });
912
913 PARENS(AST_ClassExpression, first_in_statement);
914
915 PARENS(AST_Unary, function(output) {
916 var p = output.parent();
917 return p instanceof AST_PropAccess && p.expression === this
918 || p instanceof AST_Call && p.expression === this
919 || p instanceof AST_Binary
920 && p.operator === "**"
921 && this instanceof AST_UnaryPrefix
922 && p.left === this
923 && this.operator !== "++"
924 && this.operator !== "--";
925 });
926
927 PARENS(AST_Await, function(output) {
928 var p = output.parent();
929 return p instanceof AST_PropAccess && p.expression === this
930 || p instanceof AST_Call && p.expression === this
931 || output.option("safari10") && p instanceof AST_UnaryPrefix;
932 });
933
934 PARENS(AST_Sequence, function(output) {
935 var p = output.parent();
936 return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
937 || p instanceof AST_Unary // !(foo, bar, baz)
938 || p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
939 || p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
940 || p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
941 || p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
942 || p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
943 || p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
944 * ==> 20 (side effect, set a := 10 and b := 20) */
945 || p instanceof AST_Arrow // x => (x, x)
946 || p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
947 || p instanceof AST_Expansion // [...(a, b)]
948 || p instanceof AST_ForOf && this === p.object // for (e of (foo, bar)) {}
949 || p instanceof AST_Yield // yield (foo, bar)
950 || p instanceof AST_Export // export default (foo, bar)
951 ;
952 });
953
954 PARENS(AST_Binary, function(output) {
955 var p = output.parent();
956 // (foo && bar)()
957 if (p instanceof AST_Call && p.expression === this)
958 return true;
959 // typeof (foo && bar)
960 if (p instanceof AST_Unary)
961 return true;
962 // (foo && bar)["prop"], (foo && bar).prop
963 if (p instanceof AST_PropAccess && p.expression === this)
964 return true;
965 // this deals with precedence: 3 * (2 + 1)
966 if (p instanceof AST_Binary) {
967 const po = p.operator;
968 const so = this.operator;
969
970 if (so === "??" && (po === "||" || po === "&&")) {
971 return true;
972 }
973
974 if (po === "??" && (so === "||" || so === "&&")) {
975 return true;
976 }
977
978 const pp = PRECEDENCE[po];
979 const sp = PRECEDENCE[so];
980 if (pp > sp
981 || (pp == sp
982 && (this === p.right || po == "**"))) {
983 return true;
984 }
985 }
986 });
987
988 PARENS(AST_Yield, function(output) {
989 var p = output.parent();
990 // (yield 1) + (yield 2)
991 // a = yield 3
992 if (p instanceof AST_Binary && p.operator !== "=")
993 return true;
994 // (yield 1)()
995 // new (yield 1)()
996 if (p instanceof AST_Call && p.expression === this)
997 return true;
998 // (yield 1) ? yield 2 : yield 3
999 if (p instanceof AST_Conditional && p.condition === this)
1000 return true;
1001 // -(yield 4)
1002 if (p instanceof AST_Unary)
1003 return true;
1004 // (yield x).foo
1005 // (yield x)['foo']
1006 if (p instanceof AST_PropAccess && p.expression === this)
1007 return true;
1008 });
1009
1010 PARENS(AST_PropAccess, function(output) {
1011 var p = output.parent();
1012 if (p instanceof AST_New && p.expression === this) {
1013 // i.e. new (foo.bar().baz)
1014 //
1015 // if there's one call into this subtree, then we need
1016 // parens around it too, otherwise the call will be
1017 // interpreted as passing the arguments to the upper New
1018 // expression.
1019 return walk(this, node => {
1020 if (node instanceof AST_Scope) return true;
1021 if (node instanceof AST_Call) {
1022 return walk_abort; // makes walk() return true.
1023 }
1024 });
1025 }
1026 });
1027
1028 PARENS(AST_Call, function(output) {
1029 var p = output.parent(), p1;
1030 if (p instanceof AST_New && p.expression === this
1031 || p instanceof AST_Export && p.is_default && this.expression instanceof AST_Function)
1032 return true;
1033
1034 // workaround for Safari bug.
1035 // https://bugs.webkit.org/show_bug.cgi?id=123506
1036 return this.expression instanceof AST_Function
1037 && p instanceof AST_PropAccess
1038 && p.expression === this
1039 && (p1 = output.parent(1)) instanceof AST_Assign
1040 && p1.left === p;
1041 });
1042
1043 PARENS(AST_New, function(output) {
1044 var p = output.parent();
1045 if (this.args.length === 0
1046 && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
1047 || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
1048 return true;
1049 });
1050
1051 PARENS(AST_Number, function(output) {
1052 var p = output.parent();
1053 if (p instanceof AST_PropAccess && p.expression === this) {
1054 var value = this.getValue();
1055 if (value < 0 || /^0/.test(make_num(value))) {
1056 return true;
1057 }
1058 }
1059 });
1060
1061 PARENS(AST_BigInt, function(output) {
1062 var p = output.parent();
1063 if (p instanceof AST_PropAccess && p.expression === this) {
1064 var value = this.getValue();
1065 if (value.startsWith("-")) {
1066 return true;
1067 }
1068 }
1069 });
1070
1071 PARENS([ AST_Assign, AST_Conditional ], function(output) {
1072 var p = output.parent();
1073 // !(a = false) → true
1074 if (p instanceof AST_Unary)
1075 return true;
1076 // 1 + (a = 2) + 3 → 6, side effect setting a = 2
1077 if (p instanceof AST_Binary && !(p instanceof AST_Assign))
1078 return true;
1079 // (a = func)() —or— new (a = Object)()
1080 if (p instanceof AST_Call && p.expression === this)
1081 return true;
1082 // (a = foo) ? bar : baz
1083 if (p instanceof AST_Conditional && p.condition === this)
1084 return true;
1085 // (a = foo)["prop"] —or— (a = foo).prop
1086 if (p instanceof AST_PropAccess && p.expression === this)
1087 return true;
1088 // ({a, b} = {a: 1, b: 2}), a destructuring assignment
1089 if (this instanceof AST_Assign && this.left instanceof AST_Destructuring && this.left.is_array === false)
1090 return true;
1091 });
1092
1093 /* -----[ PRINTERS ]----- */
1094
1095 DEFPRINT(AST_Directive, function(self, output) {
1096 output.print_string(self.value, self.quote);
1097 output.semicolon();
1098 });
1099
1100 DEFPRINT(AST_Expansion, function (self, output) {
1101 output.print("...");
1102 self.expression.print(output);
1103 });
1104
1105 DEFPRINT(AST_Destructuring, function (self, output) {
1106 output.print(self.is_array ? "[" : "{");
1107 var len = self.names.length;
1108 self.names.forEach(function (name, i) {
1109 if (i > 0) output.comma();
1110 name.print(output);
1111 // If the final element is a hole, we need to make sure it
1112 // doesn't look like a trailing comma, by inserting an actual
1113 // trailing comma.
1114 if (i == len - 1 && name instanceof AST_Hole) output.comma();
1115 });
1116 output.print(self.is_array ? "]" : "}");
1117 });
1118
1119 DEFPRINT(AST_Debugger, function(self, output) {
1120 output.print("debugger");
1121 output.semicolon();
1122 });
1123
1124 /* -----[ statements ]----- */
1125
1126 function display_body(body, is_toplevel, output, allow_directives) {
1127 var last = body.length - 1;
1128 output.in_directive = allow_directives;
1129 body.forEach(function(stmt, i) {
1130 if (output.in_directive === true && !(stmt instanceof AST_Directive ||
1131 stmt instanceof AST_EmptyStatement ||
1132 (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String)
1133 )) {
1134 output.in_directive = false;
1135 }
1136 if (!(stmt instanceof AST_EmptyStatement)) {
1137 output.indent();
1138 stmt.print(output);
1139 if (!(i == last && is_toplevel)) {
1140 output.newline();
1141 if (is_toplevel) output.newline();
1142 }
1143 }
1144 if (output.in_directive === true &&
1145 stmt instanceof AST_SimpleStatement &&
1146 stmt.body instanceof AST_String
1147 ) {
1148 output.in_directive = false;
1149 }
1150 });
1151 output.in_directive = false;
1152 }
1153
1154 AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output) {
1155 force_statement(this.body, output);
1156 });
1157
1158 DEFPRINT(AST_Statement, function(self, output) {
1159 self.body.print(output);
1160 output.semicolon();
1161 });
1162 DEFPRINT(AST_Toplevel, function(self, output) {
1163 display_body(self.body, true, output, true);
1164 output.print("");
1165 });
1166 DEFPRINT(AST_LabeledStatement, function(self, output) {
1167 self.label.print(output);
1168 output.colon();
1169 self.body.print(output);
1170 });
1171 DEFPRINT(AST_SimpleStatement, function(self, output) {
1172 self.body.print(output);
1173 output.semicolon();
1174 });
1175 function print_braced_empty(self, output) {
1176 output.print("{");
1177 output.with_indent(output.next_indent(), function() {
1178 output.append_comments(self, true);
1179 });
1180 output.print("}");
1181 }
1182 function print_braced(self, output, allow_directives) {
1183 if (self.body.length > 0) {
1184 output.with_block(function() {
1185 display_body(self.body, false, output, allow_directives);
1186 });
1187 } else print_braced_empty(self, output);
1188 }
1189 DEFPRINT(AST_BlockStatement, function(self, output) {
1190 print_braced(self, output);
1191 });
1192 DEFPRINT(AST_EmptyStatement, function(self, output) {
1193 output.semicolon();
1194 });
1195 DEFPRINT(AST_Do, function(self, output) {
1196 output.print("do");
1197 output.space();
1198 make_block(self.body, output);
1199 output.space();
1200 output.print("while");
1201 output.space();
1202 output.with_parens(function() {
1203 self.condition.print(output);
1204 });
1205 output.semicolon();
1206 });
1207 DEFPRINT(AST_While, function(self, output) {
1208 output.print("while");
1209 output.space();
1210 output.with_parens(function() {
1211 self.condition.print(output);
1212 });
1213 output.space();
1214 self._do_print_body(output);
1215 });
1216 DEFPRINT(AST_For, function(self, output) {
1217 output.print("for");
1218 output.space();
1219 output.with_parens(function() {
1220 if (self.init) {
1221 if (self.init instanceof AST_Definitions) {
1222 self.init.print(output);
1223 } else {
1224 parenthesize_for_noin(self.init, output, true);
1225 }
1226 output.print(";");
1227 output.space();
1228 } else {
1229 output.print(";");
1230 }
1231 if (self.condition) {
1232 self.condition.print(output);
1233 output.print(";");
1234 output.space();
1235 } else {
1236 output.print(";");
1237 }
1238 if (self.step) {
1239 self.step.print(output);
1240 }
1241 });
1242 output.space();
1243 self._do_print_body(output);
1244 });
1245 DEFPRINT(AST_ForIn, function(self, output) {
1246 output.print("for");
1247 if (self.await) {
1248 output.space();
1249 output.print("await");
1250 }
1251 output.space();
1252 output.with_parens(function() {
1253 self.init.print(output);
1254 output.space();
1255 output.print(self instanceof AST_ForOf ? "of" : "in");
1256 output.space();
1257 self.object.print(output);
1258 });
1259 output.space();
1260 self._do_print_body(output);
1261 });
1262 DEFPRINT(AST_With, function(self, output) {
1263 output.print("with");
1264 output.space();
1265 output.with_parens(function() {
1266 self.expression.print(output);
1267 });
1268 output.space();
1269 self._do_print_body(output);
1270 });
1271
1272 /* -----[ functions ]----- */
1273 AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) {
1274 var self = this;
1275 if (!nokeyword) {
1276 if (self.async) {
1277 output.print("async");
1278 output.space();
1279 }
1280 output.print("function");
1281 if (self.is_generator) {
1282 output.star();
1283 }
1284 if (self.name) {
1285 output.space();
1286 }
1287 }
1288 if (self.name instanceof AST_Symbol) {
1289 self.name.print(output);
1290 } else if (nokeyword && self.name instanceof AST_Node) {
1291 output.with_square(function() {
1292 self.name.print(output); // Computed method name
1293 });
1294 }
1295 output.with_parens(function() {
1296 self.argnames.forEach(function(arg, i) {
1297 if (i) output.comma();
1298 arg.print(output);
1299 });
1300 });
1301 output.space();
1302 print_braced(self, output, true);
1303 });
1304 DEFPRINT(AST_Lambda, function(self, output) {
1305 self._do_print(output);
1306 });
1307
1308 DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
1309 var tag = self.prefix;
1310 var parenthesize_tag = tag instanceof AST_Lambda
1311 || tag instanceof AST_Binary
1312 || tag instanceof AST_Conditional
1313 || tag instanceof AST_Sequence
1314 || tag instanceof AST_Unary
1315 || tag instanceof AST_Dot && tag.expression instanceof AST_Object;
1316 if (parenthesize_tag) output.print("(");
1317 self.prefix.print(output);
1318 if (parenthesize_tag) output.print(")");
1319 self.template_string.print(output);
1320 });
1321 DEFPRINT(AST_TemplateString, function(self, output) {
1322 var is_tagged = output.parent() instanceof AST_PrefixedTemplateString;
1323
1324 output.print("`");
1325 for (var i = 0; i < self.segments.length; i++) {
1326 if (!(self.segments[i] instanceof AST_TemplateSegment)) {
1327 output.print("${");
1328 self.segments[i].print(output);
1329 output.print("}");
1330 } else if (is_tagged) {
1331 output.print(self.segments[i].raw);
1332 } else {
1333 output.print_template_string_chars(self.segments[i].value);
1334 }
1335 }
1336 output.print("`");
1337 });
1338
1339 AST_Arrow.DEFMETHOD("_do_print", function(output) {
1340 var self = this;
1341 var parent = output.parent();
1342 var needs_parens = (parent instanceof AST_Binary && !(parent instanceof AST_Assign)) ||
1343 parent instanceof AST_Unary ||
1344 (parent instanceof AST_Call && self === parent.expression);
1345 if (needs_parens) { output.print("("); }
1346 if (self.async) {
1347 output.print("async");
1348 output.space();
1349 }
1350 if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
1351 self.argnames[0].print(output);
1352 } else {
1353 output.with_parens(function() {
1354 self.argnames.forEach(function(arg, i) {
1355 if (i) output.comma();
1356 arg.print(output);
1357 });
1358 });
1359 }
1360 output.space();
1361 output.print("=>");
1362 output.space();
1363 const first_statement = self.body[0];
1364 if (
1365 self.body.length === 1
1366 && first_statement instanceof AST_Return
1367 ) {
1368 const returned = first_statement.value;
1369 if (!returned) {
1370 output.print("{}");
1371 } else if (left_is_object(returned)) {
1372 output.print("(");
1373 returned.print(output);
1374 output.print(")");
1375 } else {
1376 returned.print(output);
1377 }
1378 } else {
1379 print_braced(self, output);
1380 }
1381 if (needs_parens) { output.print(")"); }
1382 });
1383
1384 /* -----[ exits ]----- */
1385 AST_Exit.DEFMETHOD("_do_print", function(output, kind) {
1386 output.print(kind);
1387 if (this.value) {
1388 output.space();
1389 const comments = this.value.start.comments_before;
1390 if (comments && comments.length && !output.printed_comments.has(comments)) {
1391 output.print("(");
1392 this.value.print(output);
1393 output.print(")");
1394 } else {
1395 this.value.print(output);
1396 }
1397 }
1398 output.semicolon();
1399 });
1400 DEFPRINT(AST_Return, function(self, output) {
1401 self._do_print(output, "return");
1402 });
1403 DEFPRINT(AST_Throw, function(self, output) {
1404 self._do_print(output, "throw");
1405 });
1406
1407 /* -----[ yield ]----- */
1408
1409 DEFPRINT(AST_Yield, function(self, output) {
1410 var star = self.is_star ? "*" : "";
1411 output.print("yield" + star);
1412 if (self.expression) {
1413 output.space();
1414 self.expression.print(output);
1415 }
1416 });
1417
1418 DEFPRINT(AST_Await, function(self, output) {
1419 output.print("await");
1420 output.space();
1421 var e = self.expression;
1422 var parens = !(
1423 e instanceof AST_Call
1424 || e instanceof AST_SymbolRef
1425 || e instanceof AST_PropAccess
1426 || e instanceof AST_Unary
1427 || e instanceof AST_Constant
1428 || e instanceof AST_Await
1429 || e instanceof AST_Object
1430 );
1431 if (parens) output.print("(");
1432 self.expression.print(output);
1433 if (parens) output.print(")");
1434 });
1435
1436 /* -----[ loop control ]----- */
1437 AST_LoopControl.DEFMETHOD("_do_print", function(output, kind) {
1438 output.print(kind);
1439 if (this.label) {
1440 output.space();
1441 this.label.print(output);
1442 }
1443 output.semicolon();
1444 });
1445 DEFPRINT(AST_Break, function(self, output) {
1446 self._do_print(output, "break");
1447 });
1448 DEFPRINT(AST_Continue, function(self, output) {
1449 self._do_print(output, "continue");
1450 });
1451
1452 /* -----[ if ]----- */
1453 function make_then(self, output) {
1454 var b = self.body;
1455 if (output.option("braces")
1456 || output.option("ie8") && b instanceof AST_Do)
1457 return make_block(b, output);
1458 // The squeezer replaces "block"-s that contain only a single
1459 // statement with the statement itself; technically, the AST
1460 // is correct, but this can create problems when we output an
1461 // IF having an ELSE clause where the THEN clause ends in an
1462 // IF *without* an ELSE block (then the outer ELSE would refer
1463 // to the inner IF). This function checks for this case and
1464 // adds the block braces if needed.
1465 if (!b) return output.force_semicolon();
1466 while (true) {
1467 if (b instanceof AST_If) {
1468 if (!b.alternative) {
1469 make_block(self.body, output);
1470 return;
1471 }
1472 b = b.alternative;
1473 } else if (b instanceof AST_StatementWithBody) {
1474 b = b.body;
1475 } else break;
1476 }
1477 force_statement(self.body, output);
1478 }
1479 DEFPRINT(AST_If, function(self, output) {
1480 output.print("if");
1481 output.space();
1482 output.with_parens(function() {
1483 self.condition.print(output);
1484 });
1485 output.space();
1486 if (self.alternative) {
1487 make_then(self, output);
1488 output.space();
1489 output.print("else");
1490 output.space();
1491 if (self.alternative instanceof AST_If)
1492 self.alternative.print(output);
1493 else
1494 force_statement(self.alternative, output);
1495 } else {
1496 self._do_print_body(output);
1497 }
1498 });
1499
1500 /* -----[ switch ]----- */
1501 DEFPRINT(AST_Switch, function(self, output) {
1502 output.print("switch");
1503 output.space();
1504 output.with_parens(function() {
1505 self.expression.print(output);
1506 });
1507 output.space();
1508 var last = self.body.length - 1;
1509 if (last < 0) print_braced_empty(self, output);
1510 else output.with_block(function() {
1511 self.body.forEach(function(branch, i) {
1512 output.indent(true);
1513 branch.print(output);
1514 if (i < last && branch.body.length > 0)
1515 output.newline();
1516 });
1517 });
1518 });
1519 AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) {
1520 output.newline();
1521 this.body.forEach(function(stmt) {
1522 output.indent();
1523 stmt.print(output);
1524 output.newline();
1525 });
1526 });
1527 DEFPRINT(AST_Default, function(self, output) {
1528 output.print("default:");
1529 self._do_print_body(output);
1530 });
1531 DEFPRINT(AST_Case, function(self, output) {
1532 output.print("case");
1533 output.space();
1534 self.expression.print(output);
1535 output.print(":");
1536 self._do_print_body(output);
1537 });
1538
1539 /* -----[ exceptions ]----- */
1540 DEFPRINT(AST_Try, function(self, output) {
1541 output.print("try");
1542 output.space();
1543 print_braced(self, output);
1544 if (self.bcatch) {
1545 output.space();
1546 self.bcatch.print(output);
1547 }
1548 if (self.bfinally) {
1549 output.space();
1550 self.bfinally.print(output);
1551 }
1552 });
1553 DEFPRINT(AST_Catch, function(self, output) {
1554 output.print("catch");
1555 if (self.argname) {
1556 output.space();
1557 output.with_parens(function() {
1558 self.argname.print(output);
1559 });
1560 }
1561 output.space();
1562 print_braced(self, output);
1563 });
1564 DEFPRINT(AST_Finally, function(self, output) {
1565 output.print("finally");
1566 output.space();
1567 print_braced(self, output);
1568 });
1569
1570 /* -----[ var/const ]----- */
1571 AST_Definitions.DEFMETHOD("_do_print", function(output, kind) {
1572 output.print(kind);
1573 output.space();
1574 this.definitions.forEach(function(def, i) {
1575 if (i) output.comma();
1576 def.print(output);
1577 });
1578 var p = output.parent();
1579 var in_for = p instanceof AST_For || p instanceof AST_ForIn;
1580 var output_semicolon = !in_for || p && p.init !== this;
1581 if (output_semicolon)
1582 output.semicolon();
1583 });
1584 DEFPRINT(AST_Let, function(self, output) {
1585 self._do_print(output, "let");
1586 });
1587 DEFPRINT(AST_Var, function(self, output) {
1588 self._do_print(output, "var");
1589 });
1590 DEFPRINT(AST_Const, function(self, output) {
1591 self._do_print(output, "const");
1592 });
1593 DEFPRINT(AST_Import, function(self, output) {
1594 output.print("import");
1595 output.space();
1596 if (self.imported_name) {
1597 self.imported_name.print(output);
1598 }
1599 if (self.imported_name && self.imported_names) {
1600 output.print(",");
1601 output.space();
1602 }
1603 if (self.imported_names) {
1604 if (self.imported_names.length === 1 && self.imported_names[0].foreign_name.name === "*") {
1605 self.imported_names[0].print(output);
1606 } else {
1607 output.print("{");
1608 self.imported_names.forEach(function (name_import, i) {
1609 output.space();
1610 name_import.print(output);
1611 if (i < self.imported_names.length - 1) {
1612 output.print(",");
1613 }
1614 });
1615 output.space();
1616 output.print("}");
1617 }
1618 }
1619 if (self.imported_name || self.imported_names) {
1620 output.space();
1621 output.print("from");
1622 output.space();
1623 }
1624 self.module_name.print(output);
1625 output.semicolon();
1626 });
1627 DEFPRINT(AST_ImportMeta, function(self, output) {
1628 output.print("import.meta");
1629 });
1630
1631 DEFPRINT(AST_NameMapping, function(self, output) {
1632 var is_import = output.parent() instanceof AST_Import;
1633 var definition = self.name.definition();
1634 var names_are_different =
1635 (definition && definition.mangled_name || self.name.name) !==
1636 self.foreign_name.name;
1637 if (names_are_different) {
1638 if (is_import) {
1639 output.print(self.foreign_name.name);
1640 } else {
1641 self.name.print(output);
1642 }
1643 output.space();
1644 output.print("as");
1645 output.space();
1646 if (is_import) {
1647 self.name.print(output);
1648 } else {
1649 output.print(self.foreign_name.name);
1650 }
1651 } else {
1652 self.name.print(output);
1653 }
1654 });
1655
1656 DEFPRINT(AST_Export, function(self, output) {
1657 output.print("export");
1658 output.space();
1659 if (self.is_default) {
1660 output.print("default");
1661 output.space();
1662 }
1663 if (self.exported_names) {
1664 if (self.exported_names.length === 1 && self.exported_names[0].name.name === "*") {
1665 self.exported_names[0].print(output);
1666 } else {
1667 output.print("{");
1668 self.exported_names.forEach(function(name_export, i) {
1669 output.space();
1670 name_export.print(output);
1671 if (i < self.exported_names.length - 1) {
1672 output.print(",");
1673 }
1674 });
1675 output.space();
1676 output.print("}");
1677 }
1678 } else if (self.exported_value) {
1679 self.exported_value.print(output);
1680 } else if (self.exported_definition) {
1681 self.exported_definition.print(output);
1682 if (self.exported_definition instanceof AST_Definitions) return;
1683 }
1684 if (self.module_name) {
1685 output.space();
1686 output.print("from");
1687 output.space();
1688 self.module_name.print(output);
1689 }
1690 if (self.exported_value
1691 && !(self.exported_value instanceof AST_Defun ||
1692 self.exported_value instanceof AST_Function ||
1693 self.exported_value instanceof AST_Class)
1694 || self.module_name
1695 || self.exported_names
1696 ) {
1697 output.semicolon();
1698 }
1699 });
1700
1701 function parenthesize_for_noin(node, output, noin) {
1702 var parens = false;
1703 // need to take some precautions here:
1704 // https://github.com/mishoo/UglifyJS2/issues/60
1705 if (noin) {
1706 parens = walk(node, node => {
1707 if (node instanceof AST_Scope) return true;
1708 if (node instanceof AST_Binary && node.operator == "in") {
1709 return walk_abort; // makes walk() return true
1710 }
1711 });
1712 }
1713 node.print(output, parens);
1714 }
1715
1716 DEFPRINT(AST_VarDef, function(self, output) {
1717 self.name.print(output);
1718 if (self.value) {
1719 output.space();
1720 output.print("=");
1721 output.space();
1722 var p = output.parent(1);
1723 var noin = p instanceof AST_For || p instanceof AST_ForIn;
1724 parenthesize_for_noin(self.value, output, noin);
1725 }
1726 });
1727
1728 /* -----[ other expressions ]----- */
1729 DEFPRINT(AST_Call, function(self, output) {
1730 self.expression.print(output);
1731 if (self instanceof AST_New && self.args.length === 0)
1732 return;
1733 if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
1734 output.add_mapping(self.start);
1735 }
1736 if (self.optional) output.print("?.");
1737 output.with_parens(function() {
1738 self.args.forEach(function(expr, i) {
1739 if (i) output.comma();
1740 expr.print(output);
1741 });
1742 });
1743 });
1744 DEFPRINT(AST_New, function(self, output) {
1745 output.print("new");
1746 output.space();
1747 AST_Call.prototype._codegen(self, output);
1748 });
1749
1750 AST_Sequence.DEFMETHOD("_do_print", function(output) {
1751 this.expressions.forEach(function(node, index) {
1752 if (index > 0) {
1753 output.comma();
1754 if (output.should_break()) {
1755 output.newline();
1756 output.indent();
1757 }
1758 }
1759 node.print(output);
1760 });
1761 });
1762 DEFPRINT(AST_Sequence, function(self, output) {
1763 self._do_print(output);
1764 // var p = output.parent();
1765 // if (p instanceof AST_Statement) {
1766 // output.with_indent(output.next_indent(), function(){
1767 // self._do_print(output);
1768 // });
1769 // } else {
1770 // self._do_print(output);
1771 // }
1772 });
1773 DEFPRINT(AST_Dot, function(self, output) {
1774 var expr = self.expression;
1775 expr.print(output);
1776 var prop = self.property;
1777 var print_computed = RESERVED_WORDS.has(prop)
1778 ? output.option("ie8")
1779 : !is_identifier_string(prop, output.option("ecma") >= 2015);
1780
1781 if (self.optional) output.print("?.");
1782
1783 if (print_computed) {
1784 output.print("[");
1785 output.add_mapping(self.end);
1786 output.print_string(prop);
1787 output.print("]");
1788 } else {
1789 if (expr instanceof AST_Number && expr.getValue() >= 0) {
1790 if (!/[xa-f.)]/i.test(output.last())) {
1791 output.print(".");
1792 }
1793 }
1794 if (!self.optional) output.print(".");
1795 // the name after dot would be mapped about here.
1796 output.add_mapping(self.end);
1797 output.print_name(prop);
1798 }
1799 });
1800 DEFPRINT(AST_Sub, function(self, output) {
1801 self.expression.print(output);
1802 if (self.optional) output.print("?.");
1803 output.print("[");
1804 self.property.print(output);
1805 output.print("]");
1806 });
1807 DEFPRINT(AST_Chain, function(self, output) {
1808 self.expression.print(output);
1809 });
1810 DEFPRINT(AST_UnaryPrefix, function(self, output) {
1811 var op = self.operator;
1812 output.print(op);
1813 if (/^[a-z]/i.test(op)
1814 || (/[+-]$/.test(op)
1815 && self.expression instanceof AST_UnaryPrefix
1816 && /^[+-]/.test(self.expression.operator))) {
1817 output.space();
1818 }
1819 self.expression.print(output);
1820 });
1821 DEFPRINT(AST_UnaryPostfix, function(self, output) {
1822 self.expression.print(output);
1823 output.print(self.operator);
1824 });
1825 DEFPRINT(AST_Binary, function(self, output) {
1826 var op = self.operator;
1827 self.left.print(output);
1828 if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
1829 && self.left instanceof AST_UnaryPostfix
1830 && self.left.operator == "--") {
1831 // space is mandatory to avoid outputting -->
1832 output.print(" ");
1833 } else {
1834 // the space is optional depending on "beautify"
1835 output.space();
1836 }
1837 output.print(op);
1838 if ((op == "<" || op == "<<")
1839 && self.right instanceof AST_UnaryPrefix
1840 && self.right.operator == "!"
1841 && self.right.expression instanceof AST_UnaryPrefix
1842 && self.right.expression.operator == "--") {
1843 // space is mandatory to avoid outputting <!--
1844 output.print(" ");
1845 } else {
1846 // the space is optional depending on "beautify"
1847 output.space();
1848 }
1849 self.right.print(output);
1850 });
1851 DEFPRINT(AST_Conditional, function(self, output) {
1852 self.condition.print(output);
1853 output.space();
1854 output.print("?");
1855 output.space();
1856 self.consequent.print(output);
1857 output.space();
1858 output.colon();
1859 self.alternative.print(output);
1860 });
1861
1862 /* -----[ literals ]----- */
1863 DEFPRINT(AST_Array, function(self, output) {
1864 output.with_square(function() {
1865 var a = self.elements, len = a.length;
1866 if (len > 0) output.space();
1867 a.forEach(function(exp, i) {
1868 if (i) output.comma();
1869 exp.print(output);
1870 // If the final element is a hole, we need to make sure it
1871 // doesn't look like a trailing comma, by inserting an actual
1872 // trailing comma.
1873 if (i === len - 1 && exp instanceof AST_Hole)
1874 output.comma();
1875 });
1876 if (len > 0) output.space();
1877 });
1878 });
1879 DEFPRINT(AST_Object, function(self, output) {
1880 if (self.properties.length > 0) output.with_block(function() {
1881 self.properties.forEach(function(prop, i) {
1882 if (i) {
1883 output.print(",");
1884 output.newline();
1885 }
1886 output.indent();
1887 prop.print(output);
1888 });
1889 output.newline();
1890 });
1891 else print_braced_empty(self, output);
1892 });
1893 DEFPRINT(AST_Class, function(self, output) {
1894 output.print("class");
1895 output.space();
1896 if (self.name) {
1897 self.name.print(output);
1898 output.space();
1899 }
1900 if (self.extends) {
1901 var parens = (
1902 !(self.extends instanceof AST_SymbolRef)
1903 && !(self.extends instanceof AST_PropAccess)
1904 && !(self.extends instanceof AST_ClassExpression)
1905 && !(self.extends instanceof AST_Function)
1906 );
1907 output.print("extends");
1908 if (parens) {
1909 output.print("(");
1910 } else {
1911 output.space();
1912 }
1913 self.extends.print(output);
1914 if (parens) {
1915 output.print(")");
1916 } else {
1917 output.space();
1918 }
1919 }
1920 if (self.properties.length > 0) output.with_block(function() {
1921 self.properties.forEach(function(prop, i) {
1922 if (i) {
1923 output.newline();
1924 }
1925 output.indent();
1926 prop.print(output);
1927 });
1928 output.newline();
1929 });
1930 else output.print("{}");
1931 });
1932 DEFPRINT(AST_NewTarget, function(self, output) {
1933 output.print("new.target");
1934 });
1935
1936 function print_property_name(key, quote, output) {
1937 if (output.option("quote_keys")) {
1938 return output.print_string(key);
1939 }
1940 if ("" + +key == key && key >= 0) {
1941 if (output.option("keep_numbers")) {
1942 return output.print(key);
1943 }
1944 return output.print(make_num(key));
1945 }
1946 var print_string = RESERVED_WORDS.has(key)
1947 ? output.option("ie8")
1948 : (
1949 output.option("ecma") < 2015
1950 ? !is_basic_identifier_string(key)
1951 : !is_identifier_string(key, true)
1952 );
1953 if (print_string || (quote && output.option("keep_quoted_props"))) {
1954 return output.print_string(key, quote);
1955 }
1956 return output.print_name(key);
1957 }
1958
1959 DEFPRINT(AST_ObjectKeyVal, function(self, output) {
1960 function get_name(self) {
1961 var def = self.definition();
1962 return def ? def.mangled_name || def.name : self.name;
1963 }
1964
1965 var allowShortHand = output.option("shorthand");
1966 if (allowShortHand &&
1967 self.value instanceof AST_Symbol &&
1968 is_identifier_string(self.key, output.option("ecma") >= 2015) &&
1969 get_name(self.value) === self.key &&
1970 !RESERVED_WORDS.has(self.key)
1971 ) {
1972 print_property_name(self.key, self.quote, output);
1973
1974 } else if (allowShortHand &&
1975 self.value instanceof AST_DefaultAssign &&
1976 self.value.left instanceof AST_Symbol &&
1977 is_identifier_string(self.key, output.option("ecma") >= 2015) &&
1978 get_name(self.value.left) === self.key
1979 ) {
1980 print_property_name(self.key, self.quote, output);
1981 output.space();
1982 output.print("=");
1983 output.space();
1984 self.value.right.print(output);
1985 } else {
1986 if (!(self.key instanceof AST_Node)) {
1987 print_property_name(self.key, self.quote, output);
1988 } else {
1989 output.with_square(function() {
1990 self.key.print(output);
1991 });
1992 }
1993 output.colon();
1994 self.value.print(output);
1995 }
1996 });
1997 DEFPRINT(AST_ClassProperty, (self, output) => {
1998 if (self.static) {
1999 output.print("static");
2000 output.space();
2001 }
2002
2003 if (self.key instanceof AST_SymbolClassProperty) {
2004 print_property_name(self.key.name, self.quote, output);
2005 } else {
2006 output.print("[");
2007 self.key.print(output);
2008 output.print("]");
2009 }
2010
2011 if (self.value) {
2012 output.print("=");
2013 self.value.print(output);
2014 }
2015
2016 output.semicolon();
2017 });
2018 AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
2019 var self = this;
2020 if (self.static) {
2021 output.print("static");
2022 output.space();
2023 }
2024 if (type) {
2025 output.print(type);
2026 output.space();
2027 }
2028 if (self.key instanceof AST_SymbolMethod) {
2029 print_property_name(self.key.name, self.quote, output);
2030 } else {
2031 output.with_square(function() {
2032 self.key.print(output);
2033 });
2034 }
2035 self.value._do_print(output, true);
2036 });
2037 DEFPRINT(AST_ObjectSetter, function(self, output) {
2038 self._print_getter_setter("set", output);
2039 });
2040 DEFPRINT(AST_ObjectGetter, function(self, output) {
2041 self._print_getter_setter("get", output);
2042 });
2043 DEFPRINT(AST_ConciseMethod, function(self, output) {
2044 var type;
2045 if (self.is_generator && self.async) {
2046 type = "async*";
2047 } else if (self.is_generator) {
2048 type = "*";
2049 } else if (self.async) {
2050 type = "async";
2051 }
2052 self._print_getter_setter(type, output);
2053 });
2054 AST_Symbol.DEFMETHOD("_do_print", function(output) {
2055 var def = this.definition();
2056 output.print_name(def ? def.mangled_name || def.name : this.name);
2057 });
2058 DEFPRINT(AST_Symbol, function (self, output) {
2059 self._do_print(output);
2060 });
2061 DEFPRINT(AST_Hole, noop);
2062 DEFPRINT(AST_This, function(self, output) {
2063 output.print("this");
2064 });
2065 DEFPRINT(AST_Super, function(self, output) {
2066 output.print("super");
2067 });
2068 DEFPRINT(AST_Constant, function(self, output) {
2069 output.print(self.getValue());
2070 });
2071 DEFPRINT(AST_String, function(self, output) {
2072 output.print_string(self.getValue(), self.quote, output.in_directive);
2073 });
2074 DEFPRINT(AST_Number, function(self, output) {
2075 if ((output.option("keep_numbers") || output.use_asm) && self.start && self.start.raw != null) {
2076 output.print(self.start.raw);
2077 } else {
2078 output.print(make_num(self.getValue()));
2079 }
2080 });
2081 DEFPRINT(AST_BigInt, function(self, output) {
2082 output.print(self.getValue() + "n");
2083 });
2084
2085 const r_slash_script = /(<\s*\/\s*script)/i;
2086 const slash_script_replace = (_, $1) => $1.replace("/", "\\/");
2087 DEFPRINT(AST_RegExp, function(self, output) {
2088 let { source, flags } = self.getValue();
2089 source = regexp_source_fix(source);
2090 flags = flags ? sort_regexp_flags(flags) : "";
2091 source = source.replace(r_slash_script, slash_script_replace);
2092 output.print(output.to_utf8(`/${source}/${flags}`));
2093 const parent = output.parent();
2094 if (
2095 parent instanceof AST_Binary
2096 && /^\w/.test(parent.operator)
2097 && parent.left === self
2098 ) {
2099 output.print(" ");
2100 }
2101 });
2102
2103 function force_statement(stat, output) {
2104 if (output.option("braces")) {
2105 make_block(stat, output);
2106 } else {
2107 if (!stat || stat instanceof AST_EmptyStatement)
2108 output.force_semicolon();
2109 else
2110 stat.print(output);
2111 }
2112 }
2113
2114 function best_of(a) {
2115 var best = a[0], len = best.length;
2116 for (var i = 1; i < a.length; ++i) {
2117 if (a[i].length < len) {
2118 best = a[i];
2119 len = best.length;
2120 }
2121 }
2122 return best;
2123 }
2124
2125 function make_num(num) {
2126 var str = num.toString(10).replace(/^0\./, ".").replace("e+", "e");
2127 var candidates = [ str ];
2128 if (Math.floor(num) === num) {
2129 if (num < 0) {
2130 candidates.push("-0x" + (-num).toString(16).toLowerCase());
2131 } else {
2132 candidates.push("0x" + num.toString(16).toLowerCase());
2133 }
2134 }
2135 var match, len, digits;
2136 if (match = /^\.0+/.exec(str)) {
2137 len = match[0].length;
2138 digits = str.slice(len);
2139 candidates.push(digits + "e-" + (digits.length + len - 1));
2140 } else if (match = /0+$/.exec(str)) {
2141 len = match[0].length;
2142 candidates.push(str.slice(0, -len) + "e" + len);
2143 } else if (match = /^(\d)\.(\d+)e(-?\d+)$/.exec(str)) {
2144 candidates.push(match[1] + match[2] + "e" + (match[3] - match[2].length));
2145 }
2146 return best_of(candidates);
2147 }
2148
2149 function make_block(stmt, output) {
2150 if (!stmt || stmt instanceof AST_EmptyStatement)
2151 output.print("{}");
2152 else if (stmt instanceof AST_BlockStatement)
2153 stmt.print(output);
2154 else output.with_block(function() {
2155 output.indent();
2156 stmt.print(output);
2157 output.newline();
2158 });
2159 }
2160
2161 /* -----[ source map generators ]----- */
2162
2163 function DEFMAP(nodetype, generator) {
2164 nodetype.forEach(function(nodetype) {
2165 nodetype.DEFMETHOD("add_source_map", generator);
2166 });
2167 }
2168
2169 DEFMAP([
2170 // We could easily add info for ALL nodes, but it seems to me that
2171 // would be quite wasteful, hence this noop in the base class.
2172 AST_Node,
2173 // since the label symbol will mark it
2174 AST_LabeledStatement,
2175 AST_Toplevel,
2176 ], noop);
2177
2178 // XXX: I'm not exactly sure if we need it for all of these nodes,
2179 // or if we should add even more.
2180 DEFMAP([
2181 AST_Array,
2182 AST_BlockStatement,
2183 AST_Catch,
2184 AST_Class,
2185 AST_Constant,
2186 AST_Debugger,
2187 AST_Definitions,
2188 AST_Directive,
2189 AST_Finally,
2190 AST_Jump,
2191 AST_Lambda,
2192 AST_New,
2193 AST_Object,
2194 AST_StatementWithBody,
2195 AST_Symbol,
2196 AST_Switch,
2197 AST_SwitchBranch,
2198 AST_TemplateString,
2199 AST_TemplateSegment,
2200 AST_Try,
2201 ], function(output) {
2202 output.add_mapping(this.start);
2203 });
2204
2205 DEFMAP([
2206 AST_ObjectGetter,
2207 AST_ObjectSetter,
2208 ], function(output) {
2209 output.add_mapping(this.start, this.key.name);
2210 });
2211
2212 DEFMAP([ AST_ObjectProperty ], function(output) {
2213 output.add_mapping(this.start, this.key);
2214 });
2215})();
2216
2217export {
2218 OutputStream,
2219};