Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / handlebars / node_modules / uglify-js / lib / output.js
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
46 var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/;
47
48 function is_some_comments(comment) {
49     // multiline comment
50     return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value);
51 }
52
53 function OutputStream(options) {
54
55     options = defaults(options, {
56         indent_start     : 0,
57         indent_level     : 4,
58         quote_keys       : false,
59         space_colon      : true,
60         ascii_only       : false,
61         unescape_regexps : false,
62         inline_script    : false,
63         width            : 80,
64         max_line_len     : false,
65         beautify         : false,
66         source_map       : null,
67         bracketize       : false,
68         semicolons       : true,
69         comments         : false,
70         shebang          : true,
71         preserve_line    : false,
72         screw_ie8        : true,
73         preamble         : null,
74         quote_style      : 0,
75         keep_quoted_props: false,
76         wrap_iife        : false,
77     }, true);
78
79     // Convert comment option to RegExp if neccessary 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         }
95         else if (typeof comments === "function") {
96             comment_filter = function(comment) {
97                 return comment.type != "comment5" && comments(this, comment);
98             };
99         }
100         else if (comments === "some") {
101             comment_filter = is_some_comments;
102         } else { // NOTE includes "all" option
103             comment_filter = return_true;
104         }
105     }
106
107     var indentation = 0;
108     var current_col = 0;
109     var current_line = 1;
110     var current_pos = 0;
111     var OUTPUT = "";
112
113     function to_ascii(str, identifier) {
114         return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
115             var code = ch.charCodeAt(0).toString(16);
116             if (code.length <= 2 && !identifier) {
117                 while (code.length < 2) code = "0" + code;
118                 return "\\x" + code;
119             } else {
120                 while (code.length < 4) code = "0" + code;
121                 return "\\u" + code;
122             }
123         });
124     };
125
126     function make_string(str, quote) {
127         var dq = 0, sq = 0;
128         str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g,
129           function(s, i){
130             switch (s) {
131               case '"': ++dq; return '"';
132               case "'": ++sq; return "'";
133               case "\\": return "\\\\";
134               case "\n": return "\\n";
135               case "\r": return "\\r";
136               case "\t": return "\\t";
137               case "\b": return "\\b";
138               case "\f": return "\\f";
139               case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
140               case "\u2028": return "\\u2028";
141               case "\u2029": return "\\u2029";
142               case "\ufeff": return "\\ufeff";
143               case "\0":
144                   return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
145             }
146             return s;
147         });
148         function quote_single() {
149             return "'" + str.replace(/\x27/g, "\\'") + "'";
150         }
151         function quote_double() {
152             return '"' + str.replace(/\x22/g, '\\"') + '"';
153         }
154         if (options.ascii_only) str = to_ascii(str);
155         switch (options.quote_style) {
156           case 1:
157             return quote_single();
158           case 2:
159             return quote_double();
160           case 3:
161             return quote == "'" ? quote_single() : quote_double();
162           default:
163             return dq > sq ? quote_single() : quote_double();
164         }
165     };
166
167     function encode_string(str, quote) {
168         var ret = make_string(str, quote);
169         if (options.inline_script) {
170             ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
171             ret = ret.replace(/\x3c!--/g, "\\x3c!--");
172             ret = ret.replace(/--\x3e/g, "--\\x3e");
173         }
174         return ret;
175     };
176
177     function make_name(name) {
178         name = name.toString();
179         if (options.ascii_only)
180             name = to_ascii(name, true);
181         return name;
182     };
183
184     function make_indent(back) {
185         return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
186     };
187
188     /* -----[ beautification/minification ]----- */
189
190     var might_need_space = false;
191     var might_need_semicolon = false;
192     var might_add_newline = 0;
193     var last = null;
194
195     function last_char() {
196         return last.charAt(last.length - 1);
197     };
198
199     var ensure_line_len = options.max_line_len ? function() {
200         if (current_col > options.max_line_len) {
201             if (might_add_newline) {
202                 var left = OUTPUT.slice(0, might_add_newline);
203                 var right = OUTPUT.slice(might_add_newline);
204                 OUTPUT = left + "\n" + right;
205                 current_line++;
206                 current_pos++;
207                 current_col = right.length;
208             }
209             if (current_col > options.max_line_len) {
210                 AST_Node.warn("Output exceeds {max_line_len} characters", options);
211             }
212         }
213         might_add_newline = 0;
214     } : noop;
215
216     var requireSemicolonChars = makePredicate("( [ + * / - , .");
217
218     function print(str) {
219         str = String(str);
220         var ch = str.charAt(0);
221         if (might_need_semicolon) {
222             might_need_semicolon = false;
223
224             if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
225                 if (options.semicolons || requireSemicolonChars(ch)) {
226                     OUTPUT += ";";
227                     current_col++;
228                     current_pos++;
229                 } else {
230                     ensure_line_len();
231                     OUTPUT += "\n";
232                     current_pos++;
233                     current_line++;
234                     current_col = 0;
235
236                     if (/^\s+$/.test(str)) {
237                         // reset the semicolon flag, since we didn't print one
238                         // now and might still have to later
239                         might_need_semicolon = true;
240                     }
241                 }
242
243                 if (!options.beautify)
244                     might_need_space = false;
245             }
246         }
247
248         if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
249             var target_line = stack[stack.length - 1].start.line;
250             while (current_line < target_line) {
251                 ensure_line_len();
252                 OUTPUT += "\n";
253                 current_pos++;
254                 current_line++;
255                 current_col = 0;
256                 might_need_space = false;
257             }
258         }
259
260         if (might_need_space) {
261             var prev = last_char();
262             if ((is_identifier_char(prev)
263                     && (is_identifier_char(ch) || ch == "\\"))
264                 || (ch == "/" && ch == prev)
265                 || ((ch == "+" || ch == "-") && ch == last))
266             {
267                 OUTPUT += " ";
268                 current_col++;
269                 current_pos++;
270             }
271             might_need_space = false;
272         }
273         OUTPUT += str;
274         current_pos += str.length;
275         var a = str.split(/\r?\n/), n = a.length - 1;
276         current_line += n;
277         current_col += a[0].length;
278         if (n > 0) {
279             ensure_line_len();
280             current_col = a[n].length;
281         }
282         last = str;
283     };
284
285     var space = options.beautify ? function() {
286         print(" ");
287     } : function() {
288         might_need_space = true;
289     };
290
291     var indent = options.beautify ? function(half) {
292         if (options.beautify) {
293             print(make_indent(half ? 0.5 : 0));
294         }
295     } : noop;
296
297     var with_indent = options.beautify ? function(col, cont) {
298         if (col === true) col = next_indent();
299         var save_indentation = indentation;
300         indentation = col;
301         var ret = cont();
302         indentation = save_indentation;
303         return ret;
304     } : function(col, cont) { return cont() };
305
306     var newline = options.beautify ? function() {
307         print("\n");
308     } : options.max_line_len ? function() {
309         ensure_line_len();
310         might_add_newline = OUTPUT.length;
311     } : noop;
312
313     var semicolon = options.beautify ? function() {
314         print(";");
315     } : function() {
316         might_need_semicolon = true;
317     };
318
319     function force_semicolon() {
320         might_need_semicolon = false;
321         print(";");
322     };
323
324     function next_indent() {
325         return indentation + options.indent_level;
326     };
327
328     function with_block(cont) {
329         var ret;
330         print("{");
331         newline();
332         with_indent(next_indent(), function(){
333             ret = cont();
334         });
335         indent();
336         print("}");
337         return ret;
338     };
339
340     function with_parens(cont) {
341         print("(");
342         //XXX: still nice to have that for argument lists
343         //var ret = with_indent(current_col, cont);
344         var ret = cont();
345         print(")");
346         return ret;
347     };
348
349     function with_square(cont) {
350         print("[");
351         //var ret = with_indent(current_col, cont);
352         var ret = cont();
353         print("]");
354         return ret;
355     };
356
357     function comma() {
358         print(",");
359         space();
360     };
361
362     function colon() {
363         print(":");
364         if (options.space_colon) space();
365     };
366
367     var add_mapping = options.source_map ? function(token, name) {
368         try {
369             if (token) options.source_map.add(
370                 token.file || "?",
371                 current_line, current_col,
372                 token.line, token.col,
373                 (!name && token.type == "name") ? token.value : name
374             );
375         } catch(ex) {
376             AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
377                 file: token.file,
378                 line: token.line,
379                 col: token.col,
380                 cline: current_line,
381                 ccol: current_col,
382                 name: name || ""
383             })
384         }
385     } : noop;
386
387     function get() {
388         if (might_add_newline) {
389             ensure_line_len();
390         }
391         return OUTPUT;
392     };
393
394     var stack = [];
395     return {
396         get             : get,
397         toString        : get,
398         indent          : indent,
399         indentation     : function() { return indentation },
400         current_width   : function() { return current_col - indentation },
401         should_break    : function() { return options.width && this.current_width() >= options.width },
402         newline         : newline,
403         print           : print,
404         space           : space,
405         comma           : comma,
406         colon           : colon,
407         last            : function() { return last },
408         semicolon       : semicolon,
409         force_semicolon : force_semicolon,
410         to_ascii        : to_ascii,
411         print_name      : function(name) { print(make_name(name)) },
412         print_string    : function(str, quote, escape_directive) {
413             var encoded = encode_string(str, quote);
414             if (escape_directive === true && encoded.indexOf("\\") === -1) {
415                 // Insert semicolons to break directive prologue
416                 if (!EXPECT_DIRECTIVE.test(OUTPUT)) {
417                     force_semicolon();
418                 }
419                 force_semicolon();
420             }
421             print(encoded);
422         },
423         encode_string   : encode_string,
424         next_indent     : next_indent,
425         with_indent     : with_indent,
426         with_block      : with_block,
427         with_parens     : with_parens,
428         with_square     : with_square,
429         add_mapping     : add_mapping,
430         option          : function(opt) { return options[opt] },
431         comment_filter  : comment_filter,
432         line            : function() { return current_line },
433         col             : function() { return current_col },
434         pos             : function() { return current_pos },
435         push_node       : function(node) { stack.push(node) },
436         pop_node        : function() { return stack.pop() },
437         parent          : function(n) {
438             return stack[stack.length - 2 - (n || 0)];
439         }
440     };
441
442 };
443
444 /* -----[ code generators ]----- */
445
446 (function(){
447
448     /* -----[ utils ]----- */
449
450     function DEFPRINT(nodetype, generator) {
451         nodetype.DEFMETHOD("_codegen", generator);
452     };
453
454     var use_asm = false;
455     var in_directive = false;
456
457     AST_Node.DEFMETHOD("print", function(stream, force_parens){
458         var self = this, generator = self._codegen, prev_use_asm = use_asm;
459         if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) {
460             use_asm = true;
461         }
462         function doit() {
463             self.add_comments(stream);
464             self.add_source_map(stream);
465             generator(self, stream);
466         }
467         stream.push_node(self);
468         if (force_parens || self.needs_parens(stream)) {
469             stream.with_parens(doit);
470         } else {
471             doit();
472         }
473         stream.pop_node();
474         if (self instanceof AST_Scope) {
475             use_asm = prev_use_asm;
476         }
477     });
478
479     AST_Node.DEFMETHOD("print_to_string", function(options){
480         var s = OutputStream(options);
481         if (!options) s._readonly = true;
482         this.print(s);
483         return s.get();
484     });
485
486     /* -----[ comments ]----- */
487
488     AST_Node.DEFMETHOD("add_comments", function(output){
489         if (output._readonly) return;
490         var self = this;
491         var start = self.start;
492         if (start && !start._comments_dumped) {
493             start._comments_dumped = true;
494             var comments = start.comments_before || [];
495
496             // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
497             //               and https://github.com/mishoo/UglifyJS2/issues/372
498             if (self instanceof AST_Exit && self.value) {
499                 self.value.walk(new TreeWalker(function(node){
500                     if (node.start && node.start.comments_before) {
501                         comments = comments.concat(node.start.comments_before);
502                         node.start.comments_before = [];
503                     }
504                     if (node instanceof AST_Function ||
505                         node instanceof AST_Array ||
506                         node instanceof AST_Object)
507                     {
508                         return true; // don't go inside.
509                     }
510                 }));
511             }
512
513             if (comments.length > 0 && output.pos() == 0) {
514                 if (output.option("shebang") && comments[0].type == "comment5") {
515                     output.print("#!" + comments.shift().value + "\n");
516                     output.indent();
517                 }
518                 var preamble = output.option("preamble");
519                 if (preamble) {
520                     output.print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n"));
521                 }
522             }
523
524             comments = comments.filter(output.comment_filter, self);
525
526             // Keep single line comments after nlb, after nlb
527             if (!output.option("beautify") && comments.length > 0 &&
528                 /comment[134]/.test(comments[0].type) &&
529                 output.col() !== 0 && comments[0].nlb)
530             {
531                 output.print("\n");
532             }
533
534             comments.forEach(function(c){
535                 if (/comment[134]/.test(c.type)) {
536                     output.print("//" + c.value + "\n");
537                     output.indent();
538                 }
539                 else if (c.type == "comment2") {
540                     output.print("/*" + c.value + "*/");
541                     if (start.nlb) {
542                         output.print("\n");
543                         output.indent();
544                     } else {
545                         output.space();
546                     }
547                 }
548             });
549         }
550     });
551
552     /* -----[ PARENTHESES ]----- */
553
554     function PARENS(nodetype, func) {
555         if (Array.isArray(nodetype)) {
556             nodetype.forEach(function(nodetype){
557                 PARENS(nodetype, func);
558             });
559         } else {
560             nodetype.DEFMETHOD("needs_parens", func);
561         }
562     };
563
564     PARENS(AST_Node, function(){
565         return false;
566     });
567
568     // a function expression needs parens around it when it's provably
569     // the first token to appear in a statement.
570     PARENS(AST_Function, function(output){
571         if (first_in_statement(output)) {
572             return true;
573         }
574
575         if (output.option('wrap_iife')) {
576             var p = output.parent();
577             return p instanceof AST_Call && p.expression === this;
578         }
579
580         return false;
581     });
582
583     // same goes for an object literal, because otherwise it would be
584     // interpreted as a block of code.
585     PARENS(AST_Object, function(output){
586         return first_in_statement(output);
587     });
588
589     PARENS([ AST_Unary, AST_Undefined ], function(output){
590         var p = output.parent();
591         return p instanceof AST_PropAccess && p.expression === this
592             || p instanceof AST_Call && p.expression === this;
593     });
594
595     PARENS(AST_Seq, function(output){
596         var p = output.parent();
597         return p instanceof AST_Call             // (foo, bar)() or foo(1, (2, 3), 4)
598             || p instanceof AST_Unary            // !(foo, bar, baz)
599             || p instanceof AST_Binary           // 1 + (2, 3) + 4 ==> 8
600             || p instanceof AST_VarDef           // var a = (1, 2), b = a + a; ==> b == 4
601             || p instanceof AST_PropAccess       // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
602             || p instanceof AST_Array            // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
603             || p instanceof AST_ObjectProperty   // { foo: (1, 2) }.foo ==> 2
604             || p instanceof AST_Conditional      /* (false, true) ? (a = 10, b = 20) : (c = 30)
605                                                   * ==> 20 (side effect, set a := 10 and b := 20) */
606         ;
607     });
608
609     PARENS(AST_Binary, function(output){
610         var p = output.parent();
611         // (foo && bar)()
612         if (p instanceof AST_Call && p.expression === this)
613             return true;
614         // typeof (foo && bar)
615         if (p instanceof AST_Unary)
616             return true;
617         // (foo && bar)["prop"], (foo && bar).prop
618         if (p instanceof AST_PropAccess && p.expression === this)
619             return true;
620         // this deals with precedence: 3 * (2 + 1)
621         if (p instanceof AST_Binary) {
622             var po = p.operator, pp = PRECEDENCE[po];
623             var so = this.operator, sp = PRECEDENCE[so];
624             if (pp > sp
625                 || (pp == sp
626                     && this === p.right)) {
627                 return true;
628             }
629         }
630     });
631
632     PARENS(AST_PropAccess, function(output){
633         var p = output.parent();
634         if (p instanceof AST_New && p.expression === this) {
635             // i.e. new (foo.bar().baz)
636             //
637             // if there's one call into this subtree, then we need
638             // parens around it too, otherwise the call will be
639             // interpreted as passing the arguments to the upper New
640             // expression.
641             try {
642                 this.walk(new TreeWalker(function(node){
643                     if (node instanceof AST_Call) throw p;
644                 }));
645             } catch(ex) {
646                 if (ex !== p) throw ex;
647                 return true;
648             }
649         }
650     });
651
652     PARENS(AST_Call, function(output){
653         var p = output.parent(), p1;
654         if (p instanceof AST_New && p.expression === this)
655             return true;
656
657         // workaround for Safari bug.
658         // https://bugs.webkit.org/show_bug.cgi?id=123506
659         return this.expression instanceof AST_Function
660             && p instanceof AST_PropAccess
661             && p.expression === this
662             && (p1 = output.parent(1)) instanceof AST_Assign
663             && p1.left === p;
664     });
665
666     PARENS(AST_New, function(output){
667         var p = output.parent();
668         if (!need_constructor_parens(this, output)
669             && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
670                 || p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
671             return true;
672     });
673
674     PARENS(AST_Number, function(output){
675         var p = output.parent();
676         if (p instanceof AST_PropAccess && p.expression === this) {
677             var value = this.getValue();
678             if (value < 0 || /^0/.test(make_num(value))) {
679                 return true;
680             }
681         }
682     });
683
684     PARENS([ AST_Assign, AST_Conditional ], function (output){
685         var p = output.parent();
686         // !(a = false) → true
687         if (p instanceof AST_Unary)
688             return true;
689         // 1 + (a = 2) + 3 → 6, side effect setting a = 2
690         if (p instanceof AST_Binary && !(p instanceof AST_Assign))
691             return true;
692         // (a = func)() —or— new (a = Object)()
693         if (p instanceof AST_Call && p.expression === this)
694             return true;
695         // (a = foo) ? bar : baz
696         if (p instanceof AST_Conditional && p.condition === this)
697             return true;
698         // (a = foo)["prop"] —or— (a = foo).prop
699         if (p instanceof AST_PropAccess && p.expression === this)
700             return true;
701     });
702
703     /* -----[ PRINTERS ]----- */
704
705     DEFPRINT(AST_Directive, function(self, output){
706         output.print_string(self.value, self.quote);
707         output.semicolon();
708     });
709     DEFPRINT(AST_Debugger, function(self, output){
710         output.print("debugger");
711         output.semicolon();
712     });
713
714     /* -----[ statements ]----- */
715
716     function display_body(body, is_toplevel, output, allow_directives) {
717         var last = body.length - 1;
718         in_directive = allow_directives;
719         body.forEach(function(stmt, i){
720             if (in_directive === true && !(stmt instanceof AST_Directive ||
721                 stmt instanceof AST_EmptyStatement ||
722                 (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String)
723             )) {
724                 in_directive = false;
725             }
726             if (!(stmt instanceof AST_EmptyStatement)) {
727                 output.indent();
728                 stmt.print(output);
729                 if (!(i == last && is_toplevel)) {
730                     output.newline();
731                     if (is_toplevel) output.newline();
732                 }
733             }
734             if (in_directive === true &&
735                 stmt instanceof AST_SimpleStatement &&
736                 stmt.body instanceof AST_String
737             ) {
738                 in_directive = false;
739             }
740         });
741         in_directive = false;
742     };
743
744     AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){
745         force_statement(this.body, output);
746     });
747
748     DEFPRINT(AST_Statement, function(self, output){
749         self.body.print(output);
750         output.semicolon();
751     });
752     DEFPRINT(AST_Toplevel, function(self, output){
753         display_body(self.body, true, output, true);
754         output.print("");
755     });
756     DEFPRINT(AST_LabeledStatement, function(self, output){
757         self.label.print(output);
758         output.colon();
759         self.body.print(output);
760     });
761     DEFPRINT(AST_SimpleStatement, function(self, output){
762         self.body.print(output);
763         output.semicolon();
764     });
765     function print_bracketed(body, output, allow_directives) {
766         if (body.length > 0) output.with_block(function(){
767             display_body(body, false, output, allow_directives);
768         });
769         else output.print("{}");
770     };
771     DEFPRINT(AST_BlockStatement, function(self, output){
772         print_bracketed(self.body, output);
773     });
774     DEFPRINT(AST_EmptyStatement, function(self, output){
775         output.semicolon();
776     });
777     DEFPRINT(AST_Do, function(self, output){
778         output.print("do");
779         output.space();
780         self._do_print_body(output);
781         output.space();
782         output.print("while");
783         output.space();
784         output.with_parens(function(){
785             self.condition.print(output);
786         });
787         output.semicolon();
788     });
789     DEFPRINT(AST_While, function(self, output){
790         output.print("while");
791         output.space();
792         output.with_parens(function(){
793             self.condition.print(output);
794         });
795         output.space();
796         self._do_print_body(output);
797     });
798     DEFPRINT(AST_For, function(self, output){
799         output.print("for");
800         output.space();
801         output.with_parens(function(){
802             if (self.init && !(self.init instanceof AST_EmptyStatement)) {
803                 if (self.init instanceof AST_Definitions) {
804                     self.init.print(output);
805                 } else {
806                     parenthesize_for_noin(self.init, output, true);
807                 }
808                 output.print(";");
809                 output.space();
810             } else {
811                 output.print(";");
812             }
813             if (self.condition) {
814                 self.condition.print(output);
815                 output.print(";");
816                 output.space();
817             } else {
818                 output.print(";");
819             }
820             if (self.step) {
821                 self.step.print(output);
822             }
823         });
824         output.space();
825         self._do_print_body(output);
826     });
827     DEFPRINT(AST_ForIn, function(self, output){
828         output.print("for");
829         output.space();
830         output.with_parens(function(){
831             self.init.print(output);
832             output.space();
833             output.print("in");
834             output.space();
835             self.object.print(output);
836         });
837         output.space();
838         self._do_print_body(output);
839     });
840     DEFPRINT(AST_With, function(self, output){
841         output.print("with");
842         output.space();
843         output.with_parens(function(){
844             self.expression.print(output);
845         });
846         output.space();
847         self._do_print_body(output);
848     });
849
850     /* -----[ functions ]----- */
851     AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){
852         var self = this;
853         if (!nokeyword) {
854             output.print("function");
855         }
856         if (self.name) {
857             output.space();
858             self.name.print(output);
859         }
860         output.with_parens(function(){
861             self.argnames.forEach(function(arg, i){
862                 if (i) output.comma();
863                 arg.print(output);
864             });
865         });
866         output.space();
867         print_bracketed(self.body, output, true);
868     });
869     DEFPRINT(AST_Lambda, function(self, output){
870         self._do_print(output);
871     });
872
873     /* -----[ exits ]----- */
874     AST_Exit.DEFMETHOD("_do_print", function(output, kind){
875         output.print(kind);
876         if (this.value) {
877             output.space();
878             this.value.print(output);
879         }
880         output.semicolon();
881     });
882     DEFPRINT(AST_Return, function(self, output){
883         self._do_print(output, "return");
884     });
885     DEFPRINT(AST_Throw, function(self, output){
886         self._do_print(output, "throw");
887     });
888
889     /* -----[ loop control ]----- */
890     AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
891         output.print(kind);
892         if (this.label) {
893             output.space();
894             this.label.print(output);
895         }
896         output.semicolon();
897     });
898     DEFPRINT(AST_Break, function(self, output){
899         self._do_print(output, "break");
900     });
901     DEFPRINT(AST_Continue, function(self, output){
902         self._do_print(output, "continue");
903     });
904
905     /* -----[ if ]----- */
906     function make_then(self, output) {
907         if (output.option("bracketize")) {
908             make_block(self.body, output);
909             return;
910         }
911         // The squeezer replaces "block"-s that contain only a single
912         // statement with the statement itself; technically, the AST
913         // is correct, but this can create problems when we output an
914         // IF having an ELSE clause where the THEN clause ends in an
915         // IF *without* an ELSE block (then the outer ELSE would refer
916         // to the inner IF).  This function checks for this case and
917         // adds the block brackets if needed.
918         if (!self.body)
919             return output.force_semicolon();
920         if (self.body instanceof AST_Do) {
921             // Unconditionally use the if/do-while workaround for all browsers.
922             // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
923             // croaks with "syntax error" on code like this: if (foo)
924             // do ... while(cond); else ...  we need block brackets
925             // around do/while
926             make_block(self.body, output);
927             return;
928         }
929         var b = self.body;
930         while (true) {
931             if (b instanceof AST_If) {
932                 if (!b.alternative) {
933                     make_block(self.body, output);
934                     return;
935                 }
936                 b = b.alternative;
937             }
938             else if (b instanceof AST_StatementWithBody) {
939                 b = b.body;
940             }
941             else break;
942         }
943         force_statement(self.body, output);
944     };
945     DEFPRINT(AST_If, function(self, output){
946         output.print("if");
947         output.space();
948         output.with_parens(function(){
949             self.condition.print(output);
950         });
951         output.space();
952         if (self.alternative) {
953             make_then(self, output);
954             output.space();
955             output.print("else");
956             output.space();
957             if (self.alternative instanceof AST_If)
958                 self.alternative.print(output);
959             else
960                 force_statement(self.alternative, output);
961         } else {
962             self._do_print_body(output);
963         }
964     });
965
966     /* -----[ switch ]----- */
967     DEFPRINT(AST_Switch, function(self, output){
968         output.print("switch");
969         output.space();
970         output.with_parens(function(){
971             self.expression.print(output);
972         });
973         output.space();
974         if (self.body.length > 0) output.with_block(function(){
975             self.body.forEach(function(stmt, i){
976                 if (i) output.newline();
977                 output.indent(true);
978                 stmt.print(output);
979             });
980         });
981         else output.print("{}");
982     });
983     AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){
984         if (this.body.length > 0) {
985             output.newline();
986             this.body.forEach(function(stmt){
987                 output.indent();
988                 stmt.print(output);
989                 output.newline();
990             });
991         }
992     });
993     DEFPRINT(AST_Default, function(self, output){
994         output.print("default:");
995         self._do_print_body(output);
996     });
997     DEFPRINT(AST_Case, function(self, output){
998         output.print("case");
999         output.space();
1000         self.expression.print(output);
1001         output.print(":");
1002         self._do_print_body(output);
1003     });
1004
1005     /* -----[ exceptions ]----- */
1006     DEFPRINT(AST_Try, function(self, output){
1007         output.print("try");
1008         output.space();
1009         print_bracketed(self.body, output);
1010         if (self.bcatch) {
1011             output.space();
1012             self.bcatch.print(output);
1013         }
1014         if (self.bfinally) {
1015             output.space();
1016             self.bfinally.print(output);
1017         }
1018     });
1019     DEFPRINT(AST_Catch, function(self, output){
1020         output.print("catch");
1021         output.space();
1022         output.with_parens(function(){
1023             self.argname.print(output);
1024         });
1025         output.space();
1026         print_bracketed(self.body, output);
1027     });
1028     DEFPRINT(AST_Finally, function(self, output){
1029         output.print("finally");
1030         output.space();
1031         print_bracketed(self.body, output);
1032     });
1033
1034     /* -----[ var/const ]----- */
1035     AST_Definitions.DEFMETHOD("_do_print", function(output, kind){
1036         output.print(kind);
1037         output.space();
1038         this.definitions.forEach(function(def, i){
1039             if (i) output.comma();
1040             def.print(output);
1041         });
1042         var p = output.parent();
1043         var in_for = p instanceof AST_For || p instanceof AST_ForIn;
1044         var avoid_semicolon = in_for && p.init === this;
1045         if (!avoid_semicolon)
1046             output.semicolon();
1047     });
1048     DEFPRINT(AST_Var, function(self, output){
1049         self._do_print(output, "var");
1050     });
1051     DEFPRINT(AST_Const, function(self, output){
1052         self._do_print(output, "const");
1053     });
1054
1055     function parenthesize_for_noin(node, output, noin) {
1056         if (!noin) node.print(output);
1057         else try {
1058             // need to take some precautions here:
1059             //    https://github.com/mishoo/UglifyJS2/issues/60
1060             node.walk(new TreeWalker(function(node){
1061                 if (node instanceof AST_Binary && node.operator == "in")
1062                     throw output;
1063             }));
1064             node.print(output);
1065         } catch(ex) {
1066             if (ex !== output) throw ex;
1067             node.print(output, true);
1068         }
1069     };
1070
1071     DEFPRINT(AST_VarDef, function(self, output){
1072         self.name.print(output);
1073         if (self.value) {
1074             output.space();
1075             output.print("=");
1076             output.space();
1077             var p = output.parent(1);
1078             var noin = p instanceof AST_For || p instanceof AST_ForIn;
1079             parenthesize_for_noin(self.value, output, noin);
1080         }
1081     });
1082
1083     /* -----[ other expressions ]----- */
1084     DEFPRINT(AST_Call, function(self, output){
1085         self.expression.print(output);
1086         if (self instanceof AST_New && !need_constructor_parens(self, output))
1087             return;
1088         output.with_parens(function(){
1089             self.args.forEach(function(expr, i){
1090                 if (i) output.comma();
1091                 expr.print(output);
1092             });
1093         });
1094     });
1095     DEFPRINT(AST_New, function(self, output){
1096         output.print("new");
1097         output.space();
1098         AST_Call.prototype._codegen(self, output);
1099     });
1100
1101     AST_Seq.DEFMETHOD("_do_print", function(output){
1102         this.car.print(output);
1103         if (this.cdr) {
1104             output.comma();
1105             if (output.should_break()) {
1106                 output.newline();
1107                 output.indent();
1108             }
1109             this.cdr.print(output);
1110         }
1111     });
1112     DEFPRINT(AST_Seq, function(self, output){
1113         self._do_print(output);
1114         // var p = output.parent();
1115         // if (p instanceof AST_Statement) {
1116         //     output.with_indent(output.next_indent(), function(){
1117         //         self._do_print(output);
1118         //     });
1119         // } else {
1120         //     self._do_print(output);
1121         // }
1122     });
1123     DEFPRINT(AST_Dot, function(self, output){
1124         var expr = self.expression;
1125         expr.print(output);
1126         if (expr instanceof AST_Number && expr.getValue() >= 0) {
1127             if (!/[xa-f.)]/i.test(output.last())) {
1128                 output.print(".");
1129             }
1130         }
1131         output.print(".");
1132         // the name after dot would be mapped about here.
1133         output.add_mapping(self.end);
1134         output.print_name(self.property);
1135     });
1136     DEFPRINT(AST_Sub, function(self, output){
1137         self.expression.print(output);
1138         output.print("[");
1139         self.property.print(output);
1140         output.print("]");
1141     });
1142     DEFPRINT(AST_UnaryPrefix, function(self, output){
1143         var op = self.operator;
1144         output.print(op);
1145         if (/^[a-z]/i.test(op)
1146             || (/[+-]$/.test(op)
1147                 && self.expression instanceof AST_UnaryPrefix
1148                 && /^[+-]/.test(self.expression.operator))) {
1149             output.space();
1150         }
1151         self.expression.print(output);
1152     });
1153     DEFPRINT(AST_UnaryPostfix, function(self, output){
1154         self.expression.print(output);
1155         output.print(self.operator);
1156     });
1157     DEFPRINT(AST_Binary, function(self, output){
1158         var op = self.operator;
1159         self.left.print(output);
1160         if (op[0] == ">" /* ">>" ">>>" ">" ">=" */
1161             && self.left instanceof AST_UnaryPostfix
1162             && self.left.operator == "--") {
1163             // space is mandatory to avoid outputting -->
1164             output.print(" ");
1165         } else {
1166             // the space is optional depending on "beautify"
1167             output.space();
1168         }
1169         output.print(op);
1170         if ((op == "<" || op == "<<")
1171             && self.right instanceof AST_UnaryPrefix
1172             && self.right.operator == "!"
1173             && self.right.expression instanceof AST_UnaryPrefix
1174             && self.right.expression.operator == "--") {
1175             // space is mandatory to avoid outputting <!--
1176             output.print(" ");
1177         } else {
1178             // the space is optional depending on "beautify"
1179             output.space();
1180         }
1181         self.right.print(output);
1182     });
1183     DEFPRINT(AST_Conditional, function(self, output){
1184         self.condition.print(output);
1185         output.space();
1186         output.print("?");
1187         output.space();
1188         self.consequent.print(output);
1189         output.space();
1190         output.colon();
1191         self.alternative.print(output);
1192     });
1193
1194     /* -----[ literals ]----- */
1195     DEFPRINT(AST_Array, function(self, output){
1196         output.with_square(function(){
1197             var a = self.elements, len = a.length;
1198             if (len > 0) output.space();
1199             a.forEach(function(exp, i){
1200                 if (i) output.comma();
1201                 exp.print(output);
1202                 // If the final element is a hole, we need to make sure it
1203                 // doesn't look like a trailing comma, by inserting an actual
1204                 // trailing comma.
1205                 if (i === len - 1 && exp instanceof AST_Hole)
1206                   output.comma();
1207             });
1208             if (len > 0) output.space();
1209         });
1210     });
1211     DEFPRINT(AST_Object, function(self, output){
1212         if (self.properties.length > 0) output.with_block(function(){
1213             self.properties.forEach(function(prop, i){
1214                 if (i) {
1215                     output.print(",");
1216                     output.newline();
1217                 }
1218                 output.indent();
1219                 prop.print(output);
1220             });
1221             output.newline();
1222         });
1223         else output.print("{}");
1224     });
1225     DEFPRINT(AST_ObjectKeyVal, function(self, output){
1226         var key = self.key;
1227         var quote = self.quote;
1228         if (output.option("quote_keys")) {
1229             output.print_string(key + "");
1230         } else if ((typeof key == "number"
1231                     || !output.option("beautify")
1232                     && +key + "" == key)
1233                    && parseFloat(key) >= 0) {
1234             output.print(make_num(key));
1235         } else if (RESERVED_WORDS(key) ? output.option("screw_ie8") : is_identifier_string(key)) {
1236             if (quote && output.option("keep_quoted_props")) {
1237                 output.print_string(key, quote);
1238             } else {
1239                 output.print_name(key);
1240             }
1241         } else {
1242             output.print_string(key, quote);
1243         }
1244         output.colon();
1245         self.value.print(output);
1246     });
1247     DEFPRINT(AST_ObjectSetter, function(self, output){
1248         output.print("set");
1249         output.space();
1250         self.key.print(output);
1251         self.value._do_print(output, true);
1252     });
1253     DEFPRINT(AST_ObjectGetter, function(self, output){
1254         output.print("get");
1255         output.space();
1256         self.key.print(output);
1257         self.value._do_print(output, true);
1258     });
1259     DEFPRINT(AST_Symbol, function(self, output){
1260         var def = self.definition();
1261         output.print_name(def ? def.mangled_name || def.name : self.name);
1262     });
1263     DEFPRINT(AST_Undefined, function(self, output){
1264         output.print("void 0");
1265     });
1266     DEFPRINT(AST_Hole, noop);
1267     DEFPRINT(AST_Infinity, function(self, output){
1268         output.print("Infinity");
1269     });
1270     DEFPRINT(AST_NaN, function(self, output){
1271         output.print("NaN");
1272     });
1273     DEFPRINT(AST_This, function(self, output){
1274         output.print("this");
1275     });
1276     DEFPRINT(AST_Constant, function(self, output){
1277         output.print(self.getValue());
1278     });
1279     DEFPRINT(AST_String, function(self, output){
1280         output.print_string(self.getValue(), self.quote, in_directive);
1281     });
1282     DEFPRINT(AST_Number, function(self, output){
1283         if (use_asm && self.start && self.start.raw != null) {
1284             output.print(self.start.raw);
1285         } else {
1286             output.print(make_num(self.getValue()));
1287         }
1288     });
1289
1290     function regexp_safe_literal(code) {
1291         return [
1292             0x5c   , // \
1293             0x2f   , // /
1294             0x2e   , // .
1295             0x2b   , // +
1296             0x2a   , // *
1297             0x3f   , // ?
1298             0x28   , // (
1299             0x29   , // )
1300             0x5b   , // [
1301             0x5d   , // ]
1302             0x7b   , // {
1303             0x7d   , // }
1304             0x24   , // $
1305             0x5e   , // ^
1306             0x3a   , // :
1307             0x7c   , // |
1308             0x21   , // !
1309             0x0a   , // \n
1310             0x0d   , // \r
1311             0x00   , // \0
1312             0xfeff , // Unicode BOM
1313             0x2028 , // unicode "line separator"
1314             0x2029 , // unicode "paragraph separator"
1315         ].indexOf(code) < 0;
1316     };
1317
1318     DEFPRINT(AST_RegExp, function(self, output){
1319         var str = self.getValue().toString();
1320         if (output.option("ascii_only")) {
1321             str = output.to_ascii(str);
1322         } else if (output.option("unescape_regexps")) {
1323             str = str.split("\\\\").map(function(str){
1324                 return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
1325                     var code = parseInt(s.substr(2), 16);
1326                     return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
1327                 });
1328             }).join("\\\\");
1329         }
1330         output.print(str);
1331         var p = output.parent();
1332         if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
1333             output.print(" ");
1334     });
1335
1336     function force_statement(stat, output) {
1337         if (output.option("bracketize")) {
1338             if (!stat || stat instanceof AST_EmptyStatement)
1339                 output.print("{}");
1340             else if (stat instanceof AST_BlockStatement)
1341                 stat.print(output);
1342             else output.with_block(function(){
1343                 output.indent();
1344                 stat.print(output);
1345                 output.newline();
1346             });
1347         } else {
1348             if (!stat || stat instanceof AST_EmptyStatement)
1349                 output.force_semicolon();
1350             else
1351                 stat.print(output);
1352         }
1353     };
1354
1355     // self should be AST_New.  decide if we want to show parens or not.
1356     function need_constructor_parens(self, output) {
1357         // Always print parentheses with arguments
1358         if (self.args.length > 0) return true;
1359
1360         return output.option("beautify");
1361     };
1362
1363     function best_of(a) {
1364         var best = a[0], len = best.length;
1365         for (var i = 1; i < a.length; ++i) {
1366             if (a[i].length < len) {
1367                 best = a[i];
1368                 len = best.length;
1369             }
1370         }
1371         return best;
1372     };
1373
1374     function make_num(num) {
1375         var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m;
1376         if (Math.floor(num) === num) {
1377             if (num >= 0) {
1378                 a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
1379                        "0" + num.toString(8)); // same.
1380             } else {
1381                 a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
1382                        "-0" + (-num).toString(8)); // same.
1383             }
1384             if ((m = /^(.*?)(0+)$/.exec(num))) {
1385                 a.push(m[1] + "e" + m[2].length);
1386             }
1387         } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
1388             a.push(m[2] + "e-" + (m[1].length + m[2].length),
1389                    str.substr(str.indexOf(".")));
1390         }
1391         return best_of(a);
1392     };
1393
1394     function make_block(stmt, output) {
1395         if (stmt instanceof AST_BlockStatement) {
1396             stmt.print(output);
1397             return;
1398         }
1399         output.with_block(function(){
1400             output.indent();
1401             stmt.print(output);
1402             output.newline();
1403         });
1404     };
1405
1406     /* -----[ source map generators ]----- */
1407
1408     function DEFMAP(nodetype, generator) {
1409         nodetype.DEFMETHOD("add_source_map", function(stream){
1410             generator(this, stream);
1411         });
1412     };
1413
1414     // We could easily add info for ALL nodes, but it seems to me that
1415     // would be quite wasteful, hence this noop in the base class.
1416     DEFMAP(AST_Node, noop);
1417
1418     function basic_sourcemap_gen(self, output) {
1419         output.add_mapping(self.start);
1420     };
1421
1422     // XXX: I'm not exactly sure if we need it for all of these nodes,
1423     // or if we should add even more.
1424
1425     DEFMAP(AST_Directive, basic_sourcemap_gen);
1426     DEFMAP(AST_Debugger, basic_sourcemap_gen);
1427     DEFMAP(AST_Symbol, basic_sourcemap_gen);
1428     DEFMAP(AST_Jump, basic_sourcemap_gen);
1429     DEFMAP(AST_StatementWithBody, basic_sourcemap_gen);
1430     DEFMAP(AST_LabeledStatement, noop); // since the label symbol will mark it
1431     DEFMAP(AST_Lambda, basic_sourcemap_gen);
1432     DEFMAP(AST_Switch, basic_sourcemap_gen);
1433     DEFMAP(AST_SwitchBranch, basic_sourcemap_gen);
1434     DEFMAP(AST_BlockStatement, basic_sourcemap_gen);
1435     DEFMAP(AST_Toplevel, noop);
1436     DEFMAP(AST_New, basic_sourcemap_gen);
1437     DEFMAP(AST_Try, basic_sourcemap_gen);
1438     DEFMAP(AST_Catch, basic_sourcemap_gen);
1439     DEFMAP(AST_Finally, basic_sourcemap_gen);
1440     DEFMAP(AST_Definitions, basic_sourcemap_gen);
1441     DEFMAP(AST_Constant, basic_sourcemap_gen);
1442     DEFMAP(AST_ObjectSetter, function(self, output){
1443         output.add_mapping(self.start, self.key.name);
1444     });
1445     DEFMAP(AST_ObjectGetter, function(self, output){
1446         output.add_mapping(self.start, self.key.name);
1447     });
1448     DEFMAP(AST_ObjectProperty, function(self, output){
1449         output.add_mapping(self.start, self.key);
1450     });
1451
1452 })();