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