Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / escodegen / escodegen.js
1 /*
2   Copyright (C) 2012-2014 Yusuke Suzuki <utatane.tea@gmail.com>
3   Copyright (C) 2015 Ingvar Stepanyan <me@rreverser.com>
4   Copyright (C) 2014 Ivan Nikulin <ifaaan@gmail.com>
5   Copyright (C) 2012-2013 Michael Ficarra <escodegen.copyright@michael.ficarra.me>
6   Copyright (C) 2012-2013 Mathias Bynens <mathias@qiwi.be>
7   Copyright (C) 2013 Irakli Gozalishvili <rfobic@gmail.com>
8   Copyright (C) 2012 Robert Gust-Bardon <donate@robert.gust-bardon.org>
9   Copyright (C) 2012 John Freeman <jfreeman08@gmail.com>
10   Copyright (C) 2011-2012 Ariya Hidayat <ariya.hidayat@gmail.com>
11   Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
12   Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
13   Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
14
15   Redistribution and use in source and binary forms, with or without
16   modification, are permitted provided that the following conditions are met:
17
18     * Redistributions of source code must retain the above copyright
19       notice, this list of conditions and the following disclaimer.
20     * Redistributions in binary form must reproduce the above copyright
21       notice, this list of conditions and the following disclaimer in the
22       documentation and/or other materials provided with the distribution.
23
24   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27   ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
28   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /*global exports:true, require:true, global:true*/
37 (function () {
38     'use strict';
39
40     var Syntax,
41         Precedence,
42         BinaryPrecedence,
43         SourceNode,
44         estraverse,
45         esutils,
46         isArray,
47         base,
48         indent,
49         json,
50         renumber,
51         hexadecimal,
52         quotes,
53         escapeless,
54         newline,
55         space,
56         parentheses,
57         semicolons,
58         safeConcatenation,
59         directive,
60         extra,
61         parse,
62         sourceMap,
63         sourceCode,
64         preserveBlankLines,
65         FORMAT_MINIFY,
66         FORMAT_DEFAULTS;
67
68     estraverse = require('estraverse');
69     esutils = require('esutils');
70
71     Syntax = estraverse.Syntax;
72
73     // Generation is done by generateExpression.
74     function isExpression(node) {
75         return CodeGenerator.Expression.hasOwnProperty(node.type);
76     }
77
78     // Generation is done by generateStatement.
79     function isStatement(node) {
80         return CodeGenerator.Statement.hasOwnProperty(node.type);
81     }
82
83     Precedence = {
84         Sequence: 0,
85         Yield: 1,
86         Await: 1,
87         Assignment: 1,
88         Conditional: 2,
89         ArrowFunction: 2,
90         LogicalOR: 3,
91         LogicalAND: 4,
92         BitwiseOR: 5,
93         BitwiseXOR: 6,
94         BitwiseAND: 7,
95         Equality: 8,
96         Relational: 9,
97         BitwiseSHIFT: 10,
98         Additive: 11,
99         Multiplicative: 12,
100         Unary: 13,
101         Postfix: 14,
102         Call: 15,
103         New: 16,
104         TaggedTemplate: 17,
105         Member: 18,
106         Primary: 19
107     };
108
109     BinaryPrecedence = {
110         '||': Precedence.LogicalOR,
111         '&&': Precedence.LogicalAND,
112         '|': Precedence.BitwiseOR,
113         '^': Precedence.BitwiseXOR,
114         '&': Precedence.BitwiseAND,
115         '==': Precedence.Equality,
116         '!=': Precedence.Equality,
117         '===': Precedence.Equality,
118         '!==': Precedence.Equality,
119         'is': Precedence.Equality,
120         'isnt': Precedence.Equality,
121         '<': Precedence.Relational,
122         '>': Precedence.Relational,
123         '<=': Precedence.Relational,
124         '>=': Precedence.Relational,
125         'in': Precedence.Relational,
126         'instanceof': Precedence.Relational,
127         '<<': Precedence.BitwiseSHIFT,
128         '>>': Precedence.BitwiseSHIFT,
129         '>>>': Precedence.BitwiseSHIFT,
130         '+': Precedence.Additive,
131         '-': Precedence.Additive,
132         '*': Precedence.Multiplicative,
133         '%': Precedence.Multiplicative,
134         '/': Precedence.Multiplicative
135     };
136
137     //Flags
138     var F_ALLOW_IN = 1,
139         F_ALLOW_CALL = 1 << 1,
140         F_ALLOW_UNPARATH_NEW = 1 << 2,
141         F_FUNC_BODY = 1 << 3,
142         F_DIRECTIVE_CTX = 1 << 4,
143         F_SEMICOLON_OPT = 1 << 5;
144
145     //Expression flag sets
146     //NOTE: Flag order:
147     // F_ALLOW_IN
148     // F_ALLOW_CALL
149     // F_ALLOW_UNPARATH_NEW
150     var E_FTT = F_ALLOW_CALL | F_ALLOW_UNPARATH_NEW,
151         E_TTF = F_ALLOW_IN | F_ALLOW_CALL,
152         E_TTT = F_ALLOW_IN | F_ALLOW_CALL | F_ALLOW_UNPARATH_NEW,
153         E_TFF = F_ALLOW_IN,
154         E_FFT = F_ALLOW_UNPARATH_NEW,
155         E_TFT = F_ALLOW_IN | F_ALLOW_UNPARATH_NEW;
156
157     //Statement flag sets
158     //NOTE: Flag order:
159     // F_ALLOW_IN
160     // F_FUNC_BODY
161     // F_DIRECTIVE_CTX
162     // F_SEMICOLON_OPT
163     var S_TFFF = F_ALLOW_IN,
164         S_TFFT = F_ALLOW_IN | F_SEMICOLON_OPT,
165         S_FFFF = 0x00,
166         S_TFTF = F_ALLOW_IN | F_DIRECTIVE_CTX,
167         S_TTFF = F_ALLOW_IN | F_FUNC_BODY;
168
169     function getDefaultOptions() {
170         // default options
171         return {
172             indent: null,
173             base: null,
174             parse: null,
175             comment: false,
176             format: {
177                 indent: {
178                     style: '    ',
179                     base: 0,
180                     adjustMultilineComment: false
181                 },
182                 newline: '\n',
183                 space: ' ',
184                 json: false,
185                 renumber: false,
186                 hexadecimal: false,
187                 quotes: 'single',
188                 escapeless: false,
189                 compact: false,
190                 parentheses: true,
191                 semicolons: true,
192                 safeConcatenation: false,
193                 preserveBlankLines: false
194             },
195             moz: {
196                 comprehensionExpressionStartsWithAssignment: false,
197                 starlessGenerator: false
198             },
199             sourceMap: null,
200             sourceMapRoot: null,
201             sourceMapWithCode: false,
202             directive: false,
203             raw: true,
204             verbatim: null,
205             sourceCode: null
206         };
207     }
208
209     function stringRepeat(str, num) {
210         var result = '';
211
212         for (num |= 0; num > 0; num >>>= 1, str += str) {
213             if (num & 1) {
214                 result += str;
215             }
216         }
217
218         return result;
219     }
220
221     isArray = Array.isArray;
222     if (!isArray) {
223         isArray = function isArray(array) {
224             return Object.prototype.toString.call(array) === '[object Array]';
225         };
226     }
227
228     function hasLineTerminator(str) {
229         return (/[\r\n]/g).test(str);
230     }
231
232     function endsWithLineTerminator(str) {
233         var len = str.length;
234         return len && esutils.code.isLineTerminator(str.charCodeAt(len - 1));
235     }
236
237     function merge(target, override) {
238         var key;
239         for (key in override) {
240             if (override.hasOwnProperty(key)) {
241                 target[key] = override[key];
242             }
243         }
244         return target;
245     }
246
247     function updateDeeply(target, override) {
248         var key, val;
249
250         function isHashObject(target) {
251             return typeof target === 'object' && target instanceof Object && !(target instanceof RegExp);
252         }
253
254         for (key in override) {
255             if (override.hasOwnProperty(key)) {
256                 val = override[key];
257                 if (isHashObject(val)) {
258                     if (isHashObject(target[key])) {
259                         updateDeeply(target[key], val);
260                     } else {
261                         target[key] = updateDeeply({}, val);
262                     }
263                 } else {
264                     target[key] = val;
265                 }
266             }
267         }
268         return target;
269     }
270
271     function generateNumber(value) {
272         var result, point, temp, exponent, pos;
273
274         if (value !== value) {
275             throw new Error('Numeric literal whose value is NaN');
276         }
277         if (value < 0 || (value === 0 && 1 / value < 0)) {
278             throw new Error('Numeric literal whose value is negative');
279         }
280
281         if (value === 1 / 0) {
282             return json ? 'null' : renumber ? '1e400' : '1e+400';
283         }
284
285         result = '' + value;
286         if (!renumber || result.length < 3) {
287             return result;
288         }
289
290         point = result.indexOf('.');
291         if (!json && result.charCodeAt(0) === 0x30  /* 0 */ && point === 1) {
292             point = 0;
293             result = result.slice(1);
294         }
295         temp = result;
296         result = result.replace('e+', 'e');
297         exponent = 0;
298         if ((pos = temp.indexOf('e')) > 0) {
299             exponent = +temp.slice(pos + 1);
300             temp = temp.slice(0, pos);
301         }
302         if (point >= 0) {
303             exponent -= temp.length - point - 1;
304             temp = +(temp.slice(0, point) + temp.slice(point + 1)) + '';
305         }
306         pos = 0;
307         while (temp.charCodeAt(temp.length + pos - 1) === 0x30  /* 0 */) {
308             --pos;
309         }
310         if (pos !== 0) {
311             exponent -= pos;
312             temp = temp.slice(0, pos);
313         }
314         if (exponent !== 0) {
315             temp += 'e' + exponent;
316         }
317         if ((temp.length < result.length ||
318                     (hexadecimal && value > 1e12 && Math.floor(value) === value && (temp = '0x' + value.toString(16)).length < result.length)) &&
319                 +temp === value) {
320             result = temp;
321         }
322
323         return result;
324     }
325
326     // Generate valid RegExp expression.
327     // This function is based on https://github.com/Constellation/iv Engine
328
329     function escapeRegExpCharacter(ch, previousIsBackslash) {
330         // not handling '\' and handling \u2028 or \u2029 to unicode escape sequence
331         if ((ch & ~1) === 0x2028) {
332             return (previousIsBackslash ? 'u' : '\\u') + ((ch === 0x2028) ? '2028' : '2029');
333         } else if (ch === 10 || ch === 13) {  // \n, \r
334             return (previousIsBackslash ? '' : '\\') + ((ch === 10) ? 'n' : 'r');
335         }
336         return String.fromCharCode(ch);
337     }
338
339     function generateRegExp(reg) {
340         var match, result, flags, i, iz, ch, characterInBrack, previousIsBackslash;
341
342         result = reg.toString();
343
344         if (reg.source) {
345             // extract flag from toString result
346             match = result.match(/\/([^/]*)$/);
347             if (!match) {
348                 return result;
349             }
350
351             flags = match[1];
352             result = '';
353
354             characterInBrack = false;
355             previousIsBackslash = false;
356             for (i = 0, iz = reg.source.length; i < iz; ++i) {
357                 ch = reg.source.charCodeAt(i);
358
359                 if (!previousIsBackslash) {
360                     if (characterInBrack) {
361                         if (ch === 93) {  // ]
362                             characterInBrack = false;
363                         }
364                     } else {
365                         if (ch === 47) {  // /
366                             result += '\\';
367                         } else if (ch === 91) {  // [
368                             characterInBrack = true;
369                         }
370                     }
371                     result += escapeRegExpCharacter(ch, previousIsBackslash);
372                     previousIsBackslash = ch === 92;  // \
373                 } else {
374                     // if new RegExp("\\\n') is provided, create /\n/
375                     result += escapeRegExpCharacter(ch, previousIsBackslash);
376                     // prevent like /\\[/]/
377                     previousIsBackslash = false;
378                 }
379             }
380
381             return '/' + result + '/' + flags;
382         }
383
384         return result;
385     }
386
387     function escapeAllowedCharacter(code, next) {
388         var hex;
389
390         if (code === 0x08  /* \b */) {
391             return '\\b';
392         }
393
394         if (code === 0x0C  /* \f */) {
395             return '\\f';
396         }
397
398         if (code === 0x09  /* \t */) {
399             return '\\t';
400         }
401
402         hex = code.toString(16).toUpperCase();
403         if (json || code > 0xFF) {
404             return '\\u' + '0000'.slice(hex.length) + hex;
405         } else if (code === 0x0000 && !esutils.code.isDecimalDigit(next)) {
406             return '\\0';
407         } else if (code === 0x000B  /* \v */) { // '\v'
408             return '\\x0B';
409         } else {
410             return '\\x' + '00'.slice(hex.length) + hex;
411         }
412     }
413
414     function escapeDisallowedCharacter(code) {
415         if (code === 0x5C  /* \ */) {
416             return '\\\\';
417         }
418
419         if (code === 0x0A  /* \n */) {
420             return '\\n';
421         }
422
423         if (code === 0x0D  /* \r */) {
424             return '\\r';
425         }
426
427         if (code === 0x2028) {
428             return '\\u2028';
429         }
430
431         if (code === 0x2029) {
432             return '\\u2029';
433         }
434
435         throw new Error('Incorrectly classified character');
436     }
437
438     function escapeDirective(str) {
439         var i, iz, code, quote;
440
441         quote = quotes === 'double' ? '"' : '\'';
442         for (i = 0, iz = str.length; i < iz; ++i) {
443             code = str.charCodeAt(i);
444             if (code === 0x27  /* ' */) {
445                 quote = '"';
446                 break;
447             } else if (code === 0x22  /* " */) {
448                 quote = '\'';
449                 break;
450             } else if (code === 0x5C  /* \ */) {
451                 ++i;
452             }
453         }
454
455         return quote + str + quote;
456     }
457
458     function escapeString(str) {
459         var result = '', i, len, code, singleQuotes = 0, doubleQuotes = 0, single, quote;
460
461         for (i = 0, len = str.length; i < len; ++i) {
462             code = str.charCodeAt(i);
463             if (code === 0x27  /* ' */) {
464                 ++singleQuotes;
465             } else if (code === 0x22  /* " */) {
466                 ++doubleQuotes;
467             } else if (code === 0x2F  /* / */ && json) {
468                 result += '\\';
469             } else if (esutils.code.isLineTerminator(code) || code === 0x5C  /* \ */) {
470                 result += escapeDisallowedCharacter(code);
471                 continue;
472             } else if (!esutils.code.isIdentifierPartES5(code) && (json && code < 0x20  /* SP */ || !json && !escapeless && (code < 0x20  /* SP */ || code > 0x7E  /* ~ */))) {
473                 result += escapeAllowedCharacter(code, str.charCodeAt(i + 1));
474                 continue;
475             }
476             result += String.fromCharCode(code);
477         }
478
479         single = !(quotes === 'double' || (quotes === 'auto' && doubleQuotes < singleQuotes));
480         quote = single ? '\'' : '"';
481
482         if (!(single ? singleQuotes : doubleQuotes)) {
483             return quote + result + quote;
484         }
485
486         str = result;
487         result = quote;
488
489         for (i = 0, len = str.length; i < len; ++i) {
490             code = str.charCodeAt(i);
491             if ((code === 0x27  /* ' */ && single) || (code === 0x22  /* " */ && !single)) {
492                 result += '\\';
493             }
494             result += String.fromCharCode(code);
495         }
496
497         return result + quote;
498     }
499
500     /**
501      * flatten an array to a string, where the array can contain
502      * either strings or nested arrays
503      */
504     function flattenToString(arr) {
505         var i, iz, elem, result = '';
506         for (i = 0, iz = arr.length; i < iz; ++i) {
507             elem = arr[i];
508             result += isArray(elem) ? flattenToString(elem) : elem;
509         }
510         return result;
511     }
512
513     /**
514      * convert generated to a SourceNode when source maps are enabled.
515      */
516     function toSourceNodeWhenNeeded(generated, node) {
517         if (!sourceMap) {
518             // with no source maps, generated is either an
519             // array or a string.  if an array, flatten it.
520             // if a string, just return it
521             if (isArray(generated)) {
522                 return flattenToString(generated);
523             } else {
524                 return generated;
525             }
526         }
527         if (node == null) {
528             if (generated instanceof SourceNode) {
529                 return generated;
530             } else {
531                 node = {};
532             }
533         }
534         if (node.loc == null) {
535             return new SourceNode(null, null, sourceMap, generated, node.name || null);
536         }
537         return new SourceNode(node.loc.start.line, node.loc.start.column, (sourceMap === true ? node.loc.source || null : sourceMap), generated, node.name || null);
538     }
539
540     function noEmptySpace() {
541         return (space) ? space : ' ';
542     }
543
544     function join(left, right) {
545         var leftSource,
546             rightSource,
547             leftCharCode,
548             rightCharCode;
549
550         leftSource = toSourceNodeWhenNeeded(left).toString();
551         if (leftSource.length === 0) {
552             return [right];
553         }
554
555         rightSource = toSourceNodeWhenNeeded(right).toString();
556         if (rightSource.length === 0) {
557             return [left];
558         }
559
560         leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
561         rightCharCode = rightSource.charCodeAt(0);
562
563         if ((leftCharCode === 0x2B  /* + */ || leftCharCode === 0x2D  /* - */) && leftCharCode === rightCharCode ||
564             esutils.code.isIdentifierPartES5(leftCharCode) && esutils.code.isIdentifierPartES5(rightCharCode) ||
565             leftCharCode === 0x2F  /* / */ && rightCharCode === 0x69  /* i */) { // infix word operators all start with `i`
566             return [left, noEmptySpace(), right];
567         } else if (esutils.code.isWhiteSpace(leftCharCode) || esutils.code.isLineTerminator(leftCharCode) ||
568                 esutils.code.isWhiteSpace(rightCharCode) || esutils.code.isLineTerminator(rightCharCode)) {
569             return [left, right];
570         }
571         return [left, space, right];
572     }
573
574     function addIndent(stmt) {
575         return [base, stmt];
576     }
577
578     function withIndent(fn) {
579         var previousBase;
580         previousBase = base;
581         base += indent;
582         fn(base);
583         base = previousBase;
584     }
585
586     function calculateSpaces(str) {
587         var i;
588         for (i = str.length - 1; i >= 0; --i) {
589             if (esutils.code.isLineTerminator(str.charCodeAt(i))) {
590                 break;
591             }
592         }
593         return (str.length - 1) - i;
594     }
595
596     function adjustMultilineComment(value, specialBase) {
597         var array, i, len, line, j, spaces, previousBase, sn;
598
599         array = value.split(/\r\n|[\r\n]/);
600         spaces = Number.MAX_VALUE;
601
602         // first line doesn't have indentation
603         for (i = 1, len = array.length; i < len; ++i) {
604             line = array[i];
605             j = 0;
606             while (j < line.length && esutils.code.isWhiteSpace(line.charCodeAt(j))) {
607                 ++j;
608             }
609             if (spaces > j) {
610                 spaces = j;
611             }
612         }
613
614         if (typeof specialBase !== 'undefined') {
615             // pattern like
616             // {
617             //   var t = 20;  /*
618             //                 * this is comment
619             //                 */
620             // }
621             previousBase = base;
622             if (array[1][spaces] === '*') {
623                 specialBase += ' ';
624             }
625             base = specialBase;
626         } else {
627             if (spaces & 1) {
628                 // /*
629                 //  *
630                 //  */
631                 // If spaces are odd number, above pattern is considered.
632                 // We waste 1 space.
633                 --spaces;
634             }
635             previousBase = base;
636         }
637
638         for (i = 1, len = array.length; i < len; ++i) {
639             sn = toSourceNodeWhenNeeded(addIndent(array[i].slice(spaces)));
640             array[i] = sourceMap ? sn.join('') : sn;
641         }
642
643         base = previousBase;
644
645         return array.join('\n');
646     }
647
648     function generateComment(comment, specialBase) {
649         if (comment.type === 'Line') {
650             if (endsWithLineTerminator(comment.value)) {
651                 return '//' + comment.value;
652             } else {
653                 // Always use LineTerminator
654                 var result = '//' + comment.value;
655                 if (!preserveBlankLines) {
656                     result += '\n';
657                 }
658                 return result;
659             }
660         }
661         if (extra.format.indent.adjustMultilineComment && /[\n\r]/.test(comment.value)) {
662             return adjustMultilineComment('/*' + comment.value + '*/', specialBase);
663         }
664         return '/*' + comment.value + '*/';
665     }
666
667     function addComments(stmt, result) {
668         var i, len, comment, save, tailingToStatement, specialBase, fragment,
669             extRange, range, prevRange, prefix, infix, suffix, count;
670
671         if (stmt.leadingComments && stmt.leadingComments.length > 0) {
672             save = result;
673
674             if (preserveBlankLines) {
675                 comment = stmt.leadingComments[0];
676                 result = [];
677
678                 extRange = comment.extendedRange;
679                 range = comment.range;
680
681                 prefix = sourceCode.substring(extRange[0], range[0]);
682                 count = (prefix.match(/\n/g) || []).length;
683                 if (count > 0) {
684                     result.push(stringRepeat('\n', count));
685                     result.push(addIndent(generateComment(comment)));
686                 } else {
687                     result.push(prefix);
688                     result.push(generateComment(comment));
689                 }
690
691                 prevRange = range;
692
693                 for (i = 1, len = stmt.leadingComments.length; i < len; i++) {
694                     comment = stmt.leadingComments[i];
695                     range = comment.range;
696
697                     infix = sourceCode.substring(prevRange[1], range[0]);
698                     count = (infix.match(/\n/g) || []).length;
699                     result.push(stringRepeat('\n', count));
700                     result.push(addIndent(generateComment(comment)));
701
702                     prevRange = range;
703                 }
704
705                 suffix = sourceCode.substring(range[1], extRange[1]);
706                 count = (suffix.match(/\n/g) || []).length;
707                 result.push(stringRepeat('\n', count));
708             } else {
709                 comment = stmt.leadingComments[0];
710                 result = [];
711                 if (safeConcatenation && stmt.type === Syntax.Program && stmt.body.length === 0) {
712                     result.push('\n');
713                 }
714                 result.push(generateComment(comment));
715                 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
716                     result.push('\n');
717                 }
718
719                 for (i = 1, len = stmt.leadingComments.length; i < len; ++i) {
720                     comment = stmt.leadingComments[i];
721                     fragment = [generateComment(comment)];
722                     if (!endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
723                         fragment.push('\n');
724                     }
725                     result.push(addIndent(fragment));
726                 }
727             }
728
729             result.push(addIndent(save));
730         }
731
732         if (stmt.trailingComments) {
733
734             if (preserveBlankLines) {
735                 comment = stmt.trailingComments[0];
736                 extRange = comment.extendedRange;
737                 range = comment.range;
738
739                 prefix = sourceCode.substring(extRange[0], range[0]);
740                 count = (prefix.match(/\n/g) || []).length;
741
742                 if (count > 0) {
743                     result.push(stringRepeat('\n', count));
744                     result.push(addIndent(generateComment(comment)));
745                 } else {
746                     result.push(prefix);
747                     result.push(generateComment(comment));
748                 }
749             } else {
750                 tailingToStatement = !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString());
751                 specialBase = stringRepeat(' ', calculateSpaces(toSourceNodeWhenNeeded([base, result, indent]).toString()));
752                 for (i = 0, len = stmt.trailingComments.length; i < len; ++i) {
753                     comment = stmt.trailingComments[i];
754                     if (tailingToStatement) {
755                         // We assume target like following script
756                         //
757                         // var t = 20;  /**
758                         //               * This is comment of t
759                         //               */
760                         if (i === 0) {
761                             // first case
762                             result = [result, indent];
763                         } else {
764                             result = [result, specialBase];
765                         }
766                         result.push(generateComment(comment, specialBase));
767                     } else {
768                         result = [result, addIndent(generateComment(comment))];
769                     }
770                     if (i !== len - 1 && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
771                         result = [result, '\n'];
772                     }
773                 }
774             }
775         }
776
777         return result;
778     }
779
780     function generateBlankLines(start, end, result) {
781         var j, newlineCount = 0;
782
783         for (j = start; j < end; j++) {
784             if (sourceCode[j] === '\n') {
785                 newlineCount++;
786             }
787         }
788
789         for (j = 1; j < newlineCount; j++) {
790             result.push(newline);
791         }
792     }
793
794     function parenthesize(text, current, should) {
795         if (current < should) {
796             return ['(', text, ')'];
797         }
798         return text;
799     }
800
801     function generateVerbatimString(string) {
802         var i, iz, result;
803         result = string.split(/\r\n|\n/);
804         for (i = 1, iz = result.length; i < iz; i++) {
805             result[i] = newline + base + result[i];
806         }
807         return result;
808     }
809
810     function generateVerbatim(expr, precedence) {
811         var verbatim, result, prec;
812         verbatim = expr[extra.verbatim];
813
814         if (typeof verbatim === 'string') {
815             result = parenthesize(generateVerbatimString(verbatim), Precedence.Sequence, precedence);
816         } else {
817             // verbatim is object
818             result = generateVerbatimString(verbatim.content);
819             prec = (verbatim.precedence != null) ? verbatim.precedence : Precedence.Sequence;
820             result = parenthesize(result, prec, precedence);
821         }
822
823         return toSourceNodeWhenNeeded(result, expr);
824     }
825
826     function CodeGenerator() {
827     }
828
829     // Helpers.
830
831     CodeGenerator.prototype.maybeBlock = function(stmt, flags) {
832         var result, noLeadingComment, that = this;
833
834         noLeadingComment = !extra.comment || !stmt.leadingComments;
835
836         if (stmt.type === Syntax.BlockStatement && noLeadingComment) {
837             return [space, this.generateStatement(stmt, flags)];
838         }
839
840         if (stmt.type === Syntax.EmptyStatement && noLeadingComment) {
841             return ';';
842         }
843
844         withIndent(function () {
845             result = [
846                 newline,
847                 addIndent(that.generateStatement(stmt, flags))
848             ];
849         });
850
851         return result;
852     };
853
854     CodeGenerator.prototype.maybeBlockSuffix = function (stmt, result) {
855         var ends = endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString());
856         if (stmt.type === Syntax.BlockStatement && (!extra.comment || !stmt.leadingComments) && !ends) {
857             return [result, space];
858         }
859         if (ends) {
860             return [result, base];
861         }
862         return [result, newline, base];
863     };
864
865     function generateIdentifier(node) {
866         return toSourceNodeWhenNeeded(node.name, node);
867     }
868
869     function generateAsyncPrefix(node, spaceRequired) {
870         return node.async ? 'async' + (spaceRequired ? noEmptySpace() : space) : '';
871     }
872
873     function generateStarSuffix(node) {
874         var isGenerator = node.generator && !extra.moz.starlessGenerator;
875         return isGenerator ? '*' + space : '';
876     }
877
878     function generateMethodPrefix(prop) {
879         var func = prop.value;
880         if (func.async) {
881             return generateAsyncPrefix(func, !prop.computed);
882         } else {
883             // avoid space before method name
884             return generateStarSuffix(func) ? '*' : '';
885         }
886     }
887
888     CodeGenerator.prototype.generatePattern = function (node, precedence, flags) {
889         if (node.type === Syntax.Identifier) {
890             return generateIdentifier(node);
891         }
892         return this.generateExpression(node, precedence, flags);
893     };
894
895     CodeGenerator.prototype.generateFunctionParams = function (node) {
896         var i, iz, result, hasDefault;
897
898         hasDefault = false;
899
900         if (node.type === Syntax.ArrowFunctionExpression &&
901                 !node.rest && (!node.defaults || node.defaults.length === 0) &&
902                 node.params.length === 1 && node.params[0].type === Syntax.Identifier) {
903             // arg => { } case
904             result = [generateAsyncPrefix(node, true), generateIdentifier(node.params[0])];
905         } else {
906             result = node.type === Syntax.ArrowFunctionExpression ? [generateAsyncPrefix(node, false)] : [];
907             result.push('(');
908             if (node.defaults) {
909                 hasDefault = true;
910             }
911             for (i = 0, iz = node.params.length; i < iz; ++i) {
912                 if (hasDefault && node.defaults[i]) {
913                     // Handle default values.
914                     result.push(this.generateAssignment(node.params[i], node.defaults[i], '=', Precedence.Assignment, E_TTT));
915                 } else {
916                     result.push(this.generatePattern(node.params[i], Precedence.Assignment, E_TTT));
917                 }
918                 if (i + 1 < iz) {
919                     result.push(',' + space);
920                 }
921             }
922
923             if (node.rest) {
924                 if (node.params.length) {
925                     result.push(',' + space);
926                 }
927                 result.push('...');
928                 result.push(generateIdentifier(node.rest));
929             }
930
931             result.push(')');
932         }
933
934         return result;
935     };
936
937     CodeGenerator.prototype.generateFunctionBody = function (node) {
938         var result, expr;
939
940         result = this.generateFunctionParams(node);
941
942         if (node.type === Syntax.ArrowFunctionExpression) {
943             result.push(space);
944             result.push('=>');
945         }
946
947         if (node.expression) {
948             result.push(space);
949             expr = this.generateExpression(node.body, Precedence.Assignment, E_TTT);
950             if (expr.toString().charAt(0) === '{') {
951                 expr = ['(', expr, ')'];
952             }
953             result.push(expr);
954         } else {
955             result.push(this.maybeBlock(node.body, S_TTFF));
956         }
957
958         return result;
959     };
960
961     CodeGenerator.prototype.generateIterationForStatement = function (operator, stmt, flags) {
962         var result = ['for' + space + '('], that = this;
963         withIndent(function () {
964             if (stmt.left.type === Syntax.VariableDeclaration) {
965                 withIndent(function () {
966                     result.push(stmt.left.kind + noEmptySpace());
967                     result.push(that.generateStatement(stmt.left.declarations[0], S_FFFF));
968                 });
969             } else {
970                 result.push(that.generateExpression(stmt.left, Precedence.Call, E_TTT));
971             }
972
973             result = join(result, operator);
974             result = [join(
975                 result,
976                 that.generateExpression(stmt.right, Precedence.Sequence, E_TTT)
977             ), ')'];
978         });
979         result.push(this.maybeBlock(stmt.body, flags));
980         return result;
981     };
982
983     CodeGenerator.prototype.generatePropertyKey = function (expr, computed) {
984         var result = [];
985
986         if (computed) {
987             result.push('[');
988         }
989
990         result.push(this.generateExpression(expr, Precedence.Sequence, E_TTT));
991         if (computed) {
992             result.push(']');
993         }
994
995         return result;
996     };
997
998     CodeGenerator.prototype.generateAssignment = function (left, right, operator, precedence, flags) {
999         if (Precedence.Assignment < precedence) {
1000             flags |= F_ALLOW_IN;
1001         }
1002
1003         return parenthesize(
1004             [
1005                 this.generateExpression(left, Precedence.Call, flags),
1006                 space + operator + space,
1007                 this.generateExpression(right, Precedence.Assignment, flags)
1008             ],
1009             Precedence.Assignment,
1010             precedence
1011         );
1012     };
1013
1014     CodeGenerator.prototype.semicolon = function (flags) {
1015         if (!semicolons && flags & F_SEMICOLON_OPT) {
1016             return '';
1017         }
1018         return ';';
1019     };
1020
1021     // Statements.
1022
1023     CodeGenerator.Statement = {
1024
1025         BlockStatement: function (stmt, flags) {
1026             var range, content, result = ['{', newline], that = this;
1027
1028             withIndent(function () {
1029                 // handle functions without any code
1030                 if (stmt.body.length === 0 && preserveBlankLines) {
1031                     range = stmt.range;
1032                     if (range[1] - range[0] > 2) {
1033                         content = sourceCode.substring(range[0] + 1, range[1] - 1);
1034                         if (content[0] === '\n') {
1035                             result = ['{'];
1036                         }
1037                         result.push(content);
1038                     }
1039                 }
1040
1041                 var i, iz, fragment, bodyFlags;
1042                 bodyFlags = S_TFFF;
1043                 if (flags & F_FUNC_BODY) {
1044                     bodyFlags |= F_DIRECTIVE_CTX;
1045                 }
1046
1047                 for (i = 0, iz = stmt.body.length; i < iz; ++i) {
1048                     if (preserveBlankLines) {
1049                         // handle spaces before the first line
1050                         if (i === 0) {
1051                             if (stmt.body[0].leadingComments) {
1052                                 range = stmt.body[0].leadingComments[0].extendedRange;
1053                                 content = sourceCode.substring(range[0], range[1]);
1054                                 if (content[0] === '\n') {
1055                                     result = ['{'];
1056                                 }
1057                             }
1058                             if (!stmt.body[0].leadingComments) {
1059                                 generateBlankLines(stmt.range[0], stmt.body[0].range[0], result);
1060                             }
1061                         }
1062
1063                         // handle spaces between lines
1064                         if (i > 0) {
1065                             if (!stmt.body[i - 1].trailingComments  && !stmt.body[i].leadingComments) {
1066                                 generateBlankLines(stmt.body[i - 1].range[1], stmt.body[i].range[0], result);
1067                             }
1068                         }
1069                     }
1070
1071                     if (i === iz - 1) {
1072                         bodyFlags |= F_SEMICOLON_OPT;
1073                     }
1074
1075                     if (stmt.body[i].leadingComments && preserveBlankLines) {
1076                         fragment = that.generateStatement(stmt.body[i], bodyFlags);
1077                     } else {
1078                         fragment = addIndent(that.generateStatement(stmt.body[i], bodyFlags));
1079                     }
1080
1081                     result.push(fragment);
1082                     if (!endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1083                         if (preserveBlankLines && i < iz - 1) {
1084                             // don't add a new line if there are leading coments
1085                             // in the next statement
1086                             if (!stmt.body[i + 1].leadingComments) {
1087                                 result.push(newline);
1088                             }
1089                         } else {
1090                             result.push(newline);
1091                         }
1092                     }
1093
1094                     if (preserveBlankLines) {
1095                         // handle spaces after the last line
1096                         if (i === iz - 1) {
1097                             if (!stmt.body[i].trailingComments) {
1098                                 generateBlankLines(stmt.body[i].range[1], stmt.range[1], result);
1099                             }
1100                         }
1101                     }
1102                 }
1103             });
1104
1105             result.push(addIndent('}'));
1106             return result;
1107         },
1108
1109         BreakStatement: function (stmt, flags) {
1110             if (stmt.label) {
1111                 return 'break ' + stmt.label.name + this.semicolon(flags);
1112             }
1113             return 'break' + this.semicolon(flags);
1114         },
1115
1116         ContinueStatement: function (stmt, flags) {
1117             if (stmt.label) {
1118                 return 'continue ' + stmt.label.name + this.semicolon(flags);
1119             }
1120             return 'continue' + this.semicolon(flags);
1121         },
1122
1123         ClassBody: function (stmt, flags) {
1124             var result = [ '{', newline], that = this;
1125
1126             withIndent(function (indent) {
1127                 var i, iz;
1128
1129                 for (i = 0, iz = stmt.body.length; i < iz; ++i) {
1130                     result.push(indent);
1131                     result.push(that.generateExpression(stmt.body[i], Precedence.Sequence, E_TTT));
1132                     if (i + 1 < iz) {
1133                         result.push(newline);
1134                     }
1135                 }
1136             });
1137
1138             if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1139                 result.push(newline);
1140             }
1141             result.push(base);
1142             result.push('}');
1143             return result;
1144         },
1145
1146         ClassDeclaration: function (stmt, flags) {
1147             var result, fragment;
1148             result  = ['class ' + stmt.id.name];
1149             if (stmt.superClass) {
1150                 fragment = join('extends', this.generateExpression(stmt.superClass, Precedence.Assignment, E_TTT));
1151                 result = join(result, fragment);
1152             }
1153             result.push(space);
1154             result.push(this.generateStatement(stmt.body, S_TFFT));
1155             return result;
1156         },
1157
1158         DirectiveStatement: function (stmt, flags) {
1159             if (extra.raw && stmt.raw) {
1160                 return stmt.raw + this.semicolon(flags);
1161             }
1162             return escapeDirective(stmt.directive) + this.semicolon(flags);
1163         },
1164
1165         DoWhileStatement: function (stmt, flags) {
1166             // Because `do 42 while (cond)` is Syntax Error. We need semicolon.
1167             var result = join('do', this.maybeBlock(stmt.body, S_TFFF));
1168             result = this.maybeBlockSuffix(stmt.body, result);
1169             return join(result, [
1170                 'while' + space + '(',
1171                 this.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1172                 ')' + this.semicolon(flags)
1173             ]);
1174         },
1175
1176         CatchClause: function (stmt, flags) {
1177             var result, that = this;
1178             withIndent(function () {
1179                 var guard;
1180
1181                 result = [
1182                     'catch' + space + '(',
1183                     that.generateExpression(stmt.param, Precedence.Sequence, E_TTT),
1184                     ')'
1185                 ];
1186
1187                 if (stmt.guard) {
1188                     guard = that.generateExpression(stmt.guard, Precedence.Sequence, E_TTT);
1189                     result.splice(2, 0, ' if ', guard);
1190                 }
1191             });
1192             result.push(this.maybeBlock(stmt.body, S_TFFF));
1193             return result;
1194         },
1195
1196         DebuggerStatement: function (stmt, flags) {
1197             return 'debugger' + this.semicolon(flags);
1198         },
1199
1200         EmptyStatement: function (stmt, flags) {
1201             return ';';
1202         },
1203
1204         ExportDeclaration: function (stmt, flags) {
1205             var result = [ 'export' ], bodyFlags, that = this;
1206
1207             bodyFlags = (flags & F_SEMICOLON_OPT) ? S_TFFT : S_TFFF;
1208
1209             // export default HoistableDeclaration[Default]
1210             // export default AssignmentExpression[In] ;
1211             if (stmt['default']) {
1212                 result = join(result, 'default');
1213                 if (isStatement(stmt.declaration)) {
1214                     result = join(result, this.generateStatement(stmt.declaration, bodyFlags));
1215                 } else {
1216                     result = join(result, this.generateExpression(stmt.declaration, Precedence.Assignment, E_TTT) + this.semicolon(flags));
1217                 }
1218                 return result;
1219             }
1220
1221             // export VariableStatement
1222             // export Declaration[Default]
1223             if (stmt.declaration) {
1224                 return join(result, this.generateStatement(stmt.declaration, bodyFlags));
1225             }
1226
1227             // export * FromClause ;
1228             // export ExportClause[NoReference] FromClause ;
1229             // export ExportClause ;
1230             if (stmt.specifiers) {
1231                 if (stmt.specifiers.length === 0) {
1232                     result = join(result, '{' + space + '}');
1233                 } else if (stmt.specifiers[0].type === Syntax.ExportBatchSpecifier) {
1234                     result = join(result, this.generateExpression(stmt.specifiers[0], Precedence.Sequence, E_TTT));
1235                 } else {
1236                     result = join(result, '{');
1237                     withIndent(function (indent) {
1238                         var i, iz;
1239                         result.push(newline);
1240                         for (i = 0, iz = stmt.specifiers.length; i < iz; ++i) {
1241                             result.push(indent);
1242                             result.push(that.generateExpression(stmt.specifiers[i], Precedence.Sequence, E_TTT));
1243                             if (i + 1 < iz) {
1244                                 result.push(',' + newline);
1245                             }
1246                         }
1247                     });
1248                     if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1249                         result.push(newline);
1250                     }
1251                     result.push(base + '}');
1252                 }
1253
1254                 if (stmt.source) {
1255                     result = join(result, [
1256                         'from' + space,
1257                         // ModuleSpecifier
1258                         this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1259                         this.semicolon(flags)
1260                     ]);
1261                 } else {
1262                     result.push(this.semicolon(flags));
1263                 }
1264             }
1265             return result;
1266         },
1267
1268         ExportDefaultDeclaration: function (stmt, flags) {
1269              stmt.default = true;
1270              return this.ExportDeclaration(stmt, flags);
1271         },
1272
1273         ExportNamedDeclaration: function (stmt, flags) {
1274             return this.ExportDeclaration(stmt, flags);
1275         },
1276
1277         ExpressionStatement: function (stmt, flags) {
1278             var result, fragment;
1279
1280             function isClassPrefixed(fragment) {
1281                 var code;
1282                 if (fragment.slice(0, 5) !== 'class') {
1283                     return false;
1284                 }
1285                 code = fragment.charCodeAt(5);
1286                 return code === 0x7B  /* '{' */ || esutils.code.isWhiteSpace(code) || esutils.code.isLineTerminator(code);
1287             }
1288
1289             function isFunctionPrefixed(fragment) {
1290                 var code;
1291                 if (fragment.slice(0, 8) !== 'function') {
1292                     return false;
1293                 }
1294                 code = fragment.charCodeAt(8);
1295                 return code === 0x28 /* '(' */ || esutils.code.isWhiteSpace(code) || code === 0x2A  /* '*' */ || esutils.code.isLineTerminator(code);
1296             }
1297
1298             function isAsyncPrefixed(fragment) {
1299                 var code, i, iz;
1300                 if (fragment.slice(0, 5) !== 'async') {
1301                     return false;
1302                 }
1303                 if (!esutils.code.isWhiteSpace(fragment.charCodeAt(5))) {
1304                     return false;
1305                 }
1306                 for (i = 6, iz = fragment.length; i < iz; ++i) {
1307                     if (!esutils.code.isWhiteSpace(fragment.charCodeAt(i))) {
1308                         break;
1309                     }
1310                 }
1311                 if (i === iz) {
1312                     return false;
1313                 }
1314                 if (fragment.slice(i, i + 8) !== 'function') {
1315                     return false;
1316                 }
1317                 code = fragment.charCodeAt(i + 8);
1318                 return code === 0x28 /* '(' */ || esutils.code.isWhiteSpace(code) || code === 0x2A  /* '*' */ || esutils.code.isLineTerminator(code);
1319             }
1320
1321             result = [this.generateExpression(stmt.expression, Precedence.Sequence, E_TTT)];
1322             // 12.4 '{', 'function', 'class' is not allowed in this position.
1323             // wrap expression with parentheses
1324             fragment = toSourceNodeWhenNeeded(result).toString();
1325             if (fragment.charCodeAt(0) === 0x7B  /* '{' */ ||  // ObjectExpression
1326                     isClassPrefixed(fragment) ||
1327                     isFunctionPrefixed(fragment) ||
1328                     isAsyncPrefixed(fragment) ||
1329                     (directive && (flags & F_DIRECTIVE_CTX) && stmt.expression.type === Syntax.Literal && typeof stmt.expression.value === 'string')) {
1330                 result = ['(', result, ')' + this.semicolon(flags)];
1331             } else {
1332                 result.push(this.semicolon(flags));
1333             }
1334             return result;
1335         },
1336
1337         ImportDeclaration: function (stmt, flags) {
1338             // ES6: 15.2.1 valid import declarations:
1339             //     - import ImportClause FromClause ;
1340             //     - import ModuleSpecifier ;
1341             var result, cursor, that = this;
1342
1343             // If no ImportClause is present,
1344             // this should be `import ModuleSpecifier` so skip `from`
1345             // ModuleSpecifier is StringLiteral.
1346             if (stmt.specifiers.length === 0) {
1347                 // import ModuleSpecifier ;
1348                 return [
1349                     'import',
1350                     space,
1351                     // ModuleSpecifier
1352                     this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1353                     this.semicolon(flags)
1354                 ];
1355             }
1356
1357             // import ImportClause FromClause ;
1358             result = [
1359                 'import'
1360             ];
1361             cursor = 0;
1362
1363             // ImportedBinding
1364             if (stmt.specifiers[cursor].type === Syntax.ImportDefaultSpecifier) {
1365                 result = join(result, [
1366                         this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
1367                 ]);
1368                 ++cursor;
1369             }
1370
1371             if (stmt.specifiers[cursor]) {
1372                 if (cursor !== 0) {
1373                     result.push(',');
1374                 }
1375
1376                 if (stmt.specifiers[cursor].type === Syntax.ImportNamespaceSpecifier) {
1377                     // NameSpaceImport
1378                     result = join(result, [
1379                             space,
1380                             this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
1381                     ]);
1382                 } else {
1383                     // NamedImports
1384                     result.push(space + '{');
1385
1386                     if ((stmt.specifiers.length - cursor) === 1) {
1387                         // import { ... } from "...";
1388                         result.push(space);
1389                         result.push(this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT));
1390                         result.push(space + '}' + space);
1391                     } else {
1392                         // import {
1393                         //    ...,
1394                         //    ...,
1395                         // } from "...";
1396                         withIndent(function (indent) {
1397                             var i, iz;
1398                             result.push(newline);
1399                             for (i = cursor, iz = stmt.specifiers.length; i < iz; ++i) {
1400                                 result.push(indent);
1401                                 result.push(that.generateExpression(stmt.specifiers[i], Precedence.Sequence, E_TTT));
1402                                 if (i + 1 < iz) {
1403                                     result.push(',' + newline);
1404                                 }
1405                             }
1406                         });
1407                         if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1408                             result.push(newline);
1409                         }
1410                         result.push(base + '}' + space);
1411                     }
1412                 }
1413             }
1414
1415             result = join(result, [
1416                 'from' + space,
1417                 // ModuleSpecifier
1418                 this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1419                 this.semicolon(flags)
1420             ]);
1421             return result;
1422         },
1423
1424         VariableDeclarator: function (stmt, flags) {
1425             var itemFlags = (flags & F_ALLOW_IN) ? E_TTT : E_FTT;
1426             if (stmt.init) {
1427                 return [
1428                     this.generateExpression(stmt.id, Precedence.Assignment, itemFlags),
1429                     space,
1430                     '=',
1431                     space,
1432                     this.generateExpression(stmt.init, Precedence.Assignment, itemFlags)
1433                 ];
1434             }
1435             return this.generatePattern(stmt.id, Precedence.Assignment, itemFlags);
1436         },
1437
1438         VariableDeclaration: function (stmt, flags) {
1439             // VariableDeclarator is typed as Statement,
1440             // but joined with comma (not LineTerminator).
1441             // So if comment is attached to target node, we should specialize.
1442             var result, i, iz, node, bodyFlags, that = this;
1443
1444             result = [ stmt.kind ];
1445
1446             bodyFlags = (flags & F_ALLOW_IN) ? S_TFFF : S_FFFF;
1447
1448             function block() {
1449                 node = stmt.declarations[0];
1450                 if (extra.comment && node.leadingComments) {
1451                     result.push('\n');
1452                     result.push(addIndent(that.generateStatement(node, bodyFlags)));
1453                 } else {
1454                     result.push(noEmptySpace());
1455                     result.push(that.generateStatement(node, bodyFlags));
1456                 }
1457
1458                 for (i = 1, iz = stmt.declarations.length; i < iz; ++i) {
1459                     node = stmt.declarations[i];
1460                     if (extra.comment && node.leadingComments) {
1461                         result.push(',' + newline);
1462                         result.push(addIndent(that.generateStatement(node, bodyFlags)));
1463                     } else {
1464                         result.push(',' + space);
1465                         result.push(that.generateStatement(node, bodyFlags));
1466                     }
1467                 }
1468             }
1469
1470             if (stmt.declarations.length > 1) {
1471                 withIndent(block);
1472             } else {
1473                 block();
1474             }
1475
1476             result.push(this.semicolon(flags));
1477
1478             return result;
1479         },
1480
1481         ThrowStatement: function (stmt, flags) {
1482             return [join(
1483                 'throw',
1484                 this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
1485             ), this.semicolon(flags)];
1486         },
1487
1488         TryStatement: function (stmt, flags) {
1489             var result, i, iz, guardedHandlers;
1490
1491             result = ['try', this.maybeBlock(stmt.block, S_TFFF)];
1492             result = this.maybeBlockSuffix(stmt.block, result);
1493
1494             if (stmt.handlers) {
1495                 // old interface
1496                 for (i = 0, iz = stmt.handlers.length; i < iz; ++i) {
1497                     result = join(result, this.generateStatement(stmt.handlers[i], S_TFFF));
1498                     if (stmt.finalizer || i + 1 !== iz) {
1499                         result = this.maybeBlockSuffix(stmt.handlers[i].body, result);
1500                     }
1501                 }
1502             } else {
1503                 guardedHandlers = stmt.guardedHandlers || [];
1504
1505                 for (i = 0, iz = guardedHandlers.length; i < iz; ++i) {
1506                     result = join(result, this.generateStatement(guardedHandlers[i], S_TFFF));
1507                     if (stmt.finalizer || i + 1 !== iz) {
1508                         result = this.maybeBlockSuffix(guardedHandlers[i].body, result);
1509                     }
1510                 }
1511
1512                 // new interface
1513                 if (stmt.handler) {
1514                     if (isArray(stmt.handler)) {
1515                         for (i = 0, iz = stmt.handler.length; i < iz; ++i) {
1516                             result = join(result, this.generateStatement(stmt.handler[i], S_TFFF));
1517                             if (stmt.finalizer || i + 1 !== iz) {
1518                                 result = this.maybeBlockSuffix(stmt.handler[i].body, result);
1519                             }
1520                         }
1521                     } else {
1522                         result = join(result, this.generateStatement(stmt.handler, S_TFFF));
1523                         if (stmt.finalizer) {
1524                             result = this.maybeBlockSuffix(stmt.handler.body, result);
1525                         }
1526                     }
1527                 }
1528             }
1529             if (stmt.finalizer) {
1530                 result = join(result, ['finally', this.maybeBlock(stmt.finalizer, S_TFFF)]);
1531             }
1532             return result;
1533         },
1534
1535         SwitchStatement: function (stmt, flags) {
1536             var result, fragment, i, iz, bodyFlags, that = this;
1537             withIndent(function () {
1538                 result = [
1539                     'switch' + space + '(',
1540                     that.generateExpression(stmt.discriminant, Precedence.Sequence, E_TTT),
1541                     ')' + space + '{' + newline
1542                 ];
1543             });
1544             if (stmt.cases) {
1545                 bodyFlags = S_TFFF;
1546                 for (i = 0, iz = stmt.cases.length; i < iz; ++i) {
1547                     if (i === iz - 1) {
1548                         bodyFlags |= F_SEMICOLON_OPT;
1549                     }
1550                     fragment = addIndent(this.generateStatement(stmt.cases[i], bodyFlags));
1551                     result.push(fragment);
1552                     if (!endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1553                         result.push(newline);
1554                     }
1555                 }
1556             }
1557             result.push(addIndent('}'));
1558             return result;
1559         },
1560
1561         SwitchCase: function (stmt, flags) {
1562             var result, fragment, i, iz, bodyFlags, that = this;
1563             withIndent(function () {
1564                 if (stmt.test) {
1565                     result = [
1566                         join('case', that.generateExpression(stmt.test, Precedence.Sequence, E_TTT)),
1567                         ':'
1568                     ];
1569                 } else {
1570                     result = ['default:'];
1571                 }
1572
1573                 i = 0;
1574                 iz = stmt.consequent.length;
1575                 if (iz && stmt.consequent[0].type === Syntax.BlockStatement) {
1576                     fragment = that.maybeBlock(stmt.consequent[0], S_TFFF);
1577                     result.push(fragment);
1578                     i = 1;
1579                 }
1580
1581                 if (i !== iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1582                     result.push(newline);
1583                 }
1584
1585                 bodyFlags = S_TFFF;
1586                 for (; i < iz; ++i) {
1587                     if (i === iz - 1 && flags & F_SEMICOLON_OPT) {
1588                         bodyFlags |= F_SEMICOLON_OPT;
1589                     }
1590                     fragment = addIndent(that.generateStatement(stmt.consequent[i], bodyFlags));
1591                     result.push(fragment);
1592                     if (i + 1 !== iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1593                         result.push(newline);
1594                     }
1595                 }
1596             });
1597             return result;
1598         },
1599
1600         IfStatement: function (stmt, flags) {
1601             var result, bodyFlags, semicolonOptional, that = this;
1602             withIndent(function () {
1603                 result = [
1604                     'if' + space + '(',
1605                     that.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1606                     ')'
1607                 ];
1608             });
1609             semicolonOptional = flags & F_SEMICOLON_OPT;
1610             bodyFlags = S_TFFF;
1611             if (semicolonOptional) {
1612                 bodyFlags |= F_SEMICOLON_OPT;
1613             }
1614             if (stmt.alternate) {
1615                 result.push(this.maybeBlock(stmt.consequent, S_TFFF));
1616                 result = this.maybeBlockSuffix(stmt.consequent, result);
1617                 if (stmt.alternate.type === Syntax.IfStatement) {
1618                     result = join(result, ['else ', this.generateStatement(stmt.alternate, bodyFlags)]);
1619                 } else {
1620                     result = join(result, join('else', this.maybeBlock(stmt.alternate, bodyFlags)));
1621                 }
1622             } else {
1623                 result.push(this.maybeBlock(stmt.consequent, bodyFlags));
1624             }
1625             return result;
1626         },
1627
1628         ForStatement: function (stmt, flags) {
1629             var result, that = this;
1630             withIndent(function () {
1631                 result = ['for' + space + '('];
1632                 if (stmt.init) {
1633                     if (stmt.init.type === Syntax.VariableDeclaration) {
1634                         result.push(that.generateStatement(stmt.init, S_FFFF));
1635                     } else {
1636                         // F_ALLOW_IN becomes false.
1637                         result.push(that.generateExpression(stmt.init, Precedence.Sequence, E_FTT));
1638                         result.push(';');
1639                     }
1640                 } else {
1641                     result.push(';');
1642                 }
1643
1644                 if (stmt.test) {
1645                     result.push(space);
1646                     result.push(that.generateExpression(stmt.test, Precedence.Sequence, E_TTT));
1647                     result.push(';');
1648                 } else {
1649                     result.push(';');
1650                 }
1651
1652                 if (stmt.update) {
1653                     result.push(space);
1654                     result.push(that.generateExpression(stmt.update, Precedence.Sequence, E_TTT));
1655                     result.push(')');
1656                 } else {
1657                     result.push(')');
1658                 }
1659             });
1660
1661             result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1662             return result;
1663         },
1664
1665         ForInStatement: function (stmt, flags) {
1666             return this.generateIterationForStatement('in', stmt, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF);
1667         },
1668
1669         ForOfStatement: function (stmt, flags) {
1670             return this.generateIterationForStatement('of', stmt, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF);
1671         },
1672
1673         LabeledStatement: function (stmt, flags) {
1674             return [stmt.label.name + ':', this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF)];
1675         },
1676
1677         Program: function (stmt, flags) {
1678             var result, fragment, i, iz, bodyFlags;
1679             iz = stmt.body.length;
1680             result = [safeConcatenation && iz > 0 ? '\n' : ''];
1681             bodyFlags = S_TFTF;
1682             for (i = 0; i < iz; ++i) {
1683                 if (!safeConcatenation && i === iz - 1) {
1684                     bodyFlags |= F_SEMICOLON_OPT;
1685                 }
1686
1687                 if (preserveBlankLines) {
1688                     // handle spaces before the first line
1689                     if (i === 0) {
1690                         if (!stmt.body[0].leadingComments) {
1691                             generateBlankLines(stmt.range[0], stmt.body[i].range[0], result);
1692                         }
1693                     }
1694
1695                     // handle spaces between lines
1696                     if (i > 0) {
1697                         if (!stmt.body[i - 1].trailingComments && !stmt.body[i].leadingComments) {
1698                             generateBlankLines(stmt.body[i - 1].range[1], stmt.body[i].range[0], result);
1699                         }
1700                     }
1701                 }
1702
1703                 fragment = addIndent(this.generateStatement(stmt.body[i], bodyFlags));
1704                 result.push(fragment);
1705                 if (i + 1 < iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1706                     if (preserveBlankLines) {
1707                         if (!stmt.body[i + 1].leadingComments) {
1708                             result.push(newline);
1709                         }
1710                     } else {
1711                         result.push(newline);
1712                     }
1713                 }
1714
1715                 if (preserveBlankLines) {
1716                     // handle spaces after the last line
1717                     if (i === iz - 1) {
1718                         if (!stmt.body[i].trailingComments) {
1719                             generateBlankLines(stmt.body[i].range[1], stmt.range[1], result);
1720                         }
1721                     }
1722                 }
1723             }
1724             return result;
1725         },
1726
1727         FunctionDeclaration: function (stmt, flags) {
1728             return [
1729                 generateAsyncPrefix(stmt, true),
1730                 'function',
1731                 generateStarSuffix(stmt) || noEmptySpace(),
1732                 generateIdentifier(stmt.id),
1733                 this.generateFunctionBody(stmt)
1734             ];
1735         },
1736
1737         ReturnStatement: function (stmt, flags) {
1738             if (stmt.argument) {
1739                 return [join(
1740                     'return',
1741                     this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
1742                 ), this.semicolon(flags)];
1743             }
1744             return ['return' + this.semicolon(flags)];
1745         },
1746
1747         WhileStatement: function (stmt, flags) {
1748             var result, that = this;
1749             withIndent(function () {
1750                 result = [
1751                     'while' + space + '(',
1752                     that.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1753                     ')'
1754                 ];
1755             });
1756             result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1757             return result;
1758         },
1759
1760         WithStatement: function (stmt, flags) {
1761             var result, that = this;
1762             withIndent(function () {
1763                 result = [
1764                     'with' + space + '(',
1765                     that.generateExpression(stmt.object, Precedence.Sequence, E_TTT),
1766                     ')'
1767                 ];
1768             });
1769             result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1770             return result;
1771         }
1772
1773     };
1774
1775     merge(CodeGenerator.prototype, CodeGenerator.Statement);
1776
1777     // Expressions.
1778
1779     CodeGenerator.Expression = {
1780
1781         SequenceExpression: function (expr, precedence, flags) {
1782             var result, i, iz;
1783             if (Precedence.Sequence < precedence) {
1784                 flags |= F_ALLOW_IN;
1785             }
1786             result = [];
1787             for (i = 0, iz = expr.expressions.length; i < iz; ++i) {
1788                 result.push(this.generateExpression(expr.expressions[i], Precedence.Assignment, flags));
1789                 if (i + 1 < iz) {
1790                     result.push(',' + space);
1791                 }
1792             }
1793             return parenthesize(result, Precedence.Sequence, precedence);
1794         },
1795
1796         AssignmentExpression: function (expr, precedence, flags) {
1797             return this.generateAssignment(expr.left, expr.right, expr.operator, precedence, flags);
1798         },
1799
1800         ArrowFunctionExpression: function (expr, precedence, flags) {
1801             return parenthesize(this.generateFunctionBody(expr), Precedence.ArrowFunction, precedence);
1802         },
1803
1804         ConditionalExpression: function (expr, precedence, flags) {
1805             if (Precedence.Conditional < precedence) {
1806                 flags |= F_ALLOW_IN;
1807             }
1808             return parenthesize(
1809                 [
1810                     this.generateExpression(expr.test, Precedence.LogicalOR, flags),
1811                     space + '?' + space,
1812                     this.generateExpression(expr.consequent, Precedence.Assignment, flags),
1813                     space + ':' + space,
1814                     this.generateExpression(expr.alternate, Precedence.Assignment, flags)
1815                 ],
1816                 Precedence.Conditional,
1817                 precedence
1818             );
1819         },
1820
1821         LogicalExpression: function (expr, precedence, flags) {
1822             return this.BinaryExpression(expr, precedence, flags);
1823         },
1824
1825         BinaryExpression: function (expr, precedence, flags) {
1826             var result, currentPrecedence, fragment, leftSource;
1827             currentPrecedence = BinaryPrecedence[expr.operator];
1828
1829             if (currentPrecedence < precedence) {
1830                 flags |= F_ALLOW_IN;
1831             }
1832
1833             fragment = this.generateExpression(expr.left, currentPrecedence, flags);
1834
1835             leftSource = fragment.toString();
1836
1837             if (leftSource.charCodeAt(leftSource.length - 1) === 0x2F /* / */ && esutils.code.isIdentifierPartES5(expr.operator.charCodeAt(0))) {
1838                 result = [fragment, noEmptySpace(), expr.operator];
1839             } else {
1840                 result = join(fragment, expr.operator);
1841             }
1842
1843             fragment = this.generateExpression(expr.right, currentPrecedence + 1, flags);
1844
1845             if (expr.operator === '/' && fragment.toString().charAt(0) === '/' ||
1846             expr.operator.slice(-1) === '<' && fragment.toString().slice(0, 3) === '!--') {
1847                 // If '/' concats with '/' or `<` concats with `!--`, it is interpreted as comment start
1848                 result.push(noEmptySpace());
1849                 result.push(fragment);
1850             } else {
1851                 result = join(result, fragment);
1852             }
1853
1854             if (expr.operator === 'in' && !(flags & F_ALLOW_IN)) {
1855                 return ['(', result, ')'];
1856             }
1857             return parenthesize(result, currentPrecedence, precedence);
1858         },
1859
1860         CallExpression: function (expr, precedence, flags) {
1861             var result, i, iz;
1862             // F_ALLOW_UNPARATH_NEW becomes false.
1863             result = [this.generateExpression(expr.callee, Precedence.Call, E_TTF)];
1864             result.push('(');
1865             for (i = 0, iz = expr['arguments'].length; i < iz; ++i) {
1866                 result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
1867                 if (i + 1 < iz) {
1868                     result.push(',' + space);
1869                 }
1870             }
1871             result.push(')');
1872
1873             if (!(flags & F_ALLOW_CALL)) {
1874                 return ['(', result, ')'];
1875             }
1876             return parenthesize(result, Precedence.Call, precedence);
1877         },
1878
1879         NewExpression: function (expr, precedence, flags) {
1880             var result, length, i, iz, itemFlags;
1881             length = expr['arguments'].length;
1882
1883             // F_ALLOW_CALL becomes false.
1884             // F_ALLOW_UNPARATH_NEW may become false.
1885             itemFlags = (flags & F_ALLOW_UNPARATH_NEW && !parentheses && length === 0) ? E_TFT : E_TFF;
1886
1887             result = join(
1888                 'new',
1889                 this.generateExpression(expr.callee, Precedence.New, itemFlags)
1890             );
1891
1892             if (!(flags & F_ALLOW_UNPARATH_NEW) || parentheses || length > 0) {
1893                 result.push('(');
1894                 for (i = 0, iz = length; i < iz; ++i) {
1895                     result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
1896                     if (i + 1 < iz) {
1897                         result.push(',' + space);
1898                     }
1899                 }
1900                 result.push(')');
1901             }
1902
1903             return parenthesize(result, Precedence.New, precedence);
1904         },
1905
1906         MemberExpression: function (expr, precedence, flags) {
1907             var result, fragment;
1908
1909             // F_ALLOW_UNPARATH_NEW becomes false.
1910             result = [this.generateExpression(expr.object, Precedence.Call, (flags & F_ALLOW_CALL) ? E_TTF : E_TFF)];
1911
1912             if (expr.computed) {
1913                 result.push('[');
1914                 result.push(this.generateExpression(expr.property, Precedence.Sequence, flags & F_ALLOW_CALL ? E_TTT : E_TFT));
1915                 result.push(']');
1916             } else {
1917                 if (expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') {
1918                     fragment = toSourceNodeWhenNeeded(result).toString();
1919                     // When the following conditions are all true,
1920                     //   1. No floating point
1921                     //   2. Don't have exponents
1922                     //   3. The last character is a decimal digit
1923                     //   4. Not hexadecimal OR octal number literal
1924                     // we should add a floating point.
1925                     if (
1926                             fragment.indexOf('.') < 0 &&
1927                             !/[eExX]/.test(fragment) &&
1928                             esutils.code.isDecimalDigit(fragment.charCodeAt(fragment.length - 1)) &&
1929                             !(fragment.length >= 2 && fragment.charCodeAt(0) === 48)  // '0'
1930                             ) {
1931                         result.push('.');
1932                     }
1933                 }
1934                 result.push('.');
1935                 result.push(generateIdentifier(expr.property));
1936             }
1937
1938             return parenthesize(result, Precedence.Member, precedence);
1939         },
1940
1941         UnaryExpression: function (expr, precedence, flags) {
1942             var result, fragment, rightCharCode, leftSource, leftCharCode;
1943             fragment = this.generateExpression(expr.argument, Precedence.Unary, E_TTT);
1944
1945             if (space === '') {
1946                 result = join(expr.operator, fragment);
1947             } else {
1948                 result = [expr.operator];
1949                 if (expr.operator.length > 2) {
1950                     // delete, void, typeof
1951                     // get `typeof []`, not `typeof[]`
1952                     result = join(result, fragment);
1953                 } else {
1954                     // Prevent inserting spaces between operator and argument if it is unnecessary
1955                     // like, `!cond`
1956                     leftSource = toSourceNodeWhenNeeded(result).toString();
1957                     leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
1958                     rightCharCode = fragment.toString().charCodeAt(0);
1959
1960                     if (((leftCharCode === 0x2B  /* + */ || leftCharCode === 0x2D  /* - */) && leftCharCode === rightCharCode) ||
1961                             (esutils.code.isIdentifierPartES5(leftCharCode) && esutils.code.isIdentifierPartES5(rightCharCode))) {
1962                         result.push(noEmptySpace());
1963                         result.push(fragment);
1964                     } else {
1965                         result.push(fragment);
1966                     }
1967                 }
1968             }
1969             return parenthesize(result, Precedence.Unary, precedence);
1970         },
1971
1972         YieldExpression: function (expr, precedence, flags) {
1973             var result;
1974             if (expr.delegate) {
1975                 result = 'yield*';
1976             } else {
1977                 result = 'yield';
1978             }
1979             if (expr.argument) {
1980                 result = join(
1981                     result,
1982                     this.generateExpression(expr.argument, Precedence.Yield, E_TTT)
1983                 );
1984             }
1985             return parenthesize(result, Precedence.Yield, precedence);
1986         },
1987
1988         AwaitExpression: function (expr, precedence, flags) {
1989             var result = join(
1990                 expr.all ? 'await*' : 'await',
1991                 this.generateExpression(expr.argument, Precedence.Await, E_TTT)
1992             );
1993             return parenthesize(result, Precedence.Await, precedence);
1994         },
1995
1996         UpdateExpression: function (expr, precedence, flags) {
1997             if (expr.prefix) {
1998                 return parenthesize(
1999                     [
2000                         expr.operator,
2001                         this.generateExpression(expr.argument, Precedence.Unary, E_TTT)
2002                     ],
2003                     Precedence.Unary,
2004                     precedence
2005                 );
2006             }
2007             return parenthesize(
2008                 [
2009                     this.generateExpression(expr.argument, Precedence.Postfix, E_TTT),
2010                     expr.operator
2011                 ],
2012                 Precedence.Postfix,
2013                 precedence
2014             );
2015         },
2016
2017         FunctionExpression: function (expr, precedence, flags) {
2018             var result = [
2019                 generateAsyncPrefix(expr, true),
2020                 'function'
2021             ];
2022             if (expr.id) {
2023                 result.push(generateStarSuffix(expr) || noEmptySpace());
2024                 result.push(generateIdentifier(expr.id));
2025             } else {
2026                 result.push(generateStarSuffix(expr) || space);
2027             }
2028             result.push(this.generateFunctionBody(expr));
2029             return result;
2030         },
2031
2032         ExportBatchSpecifier: function (expr, precedence, flags) {
2033             return '*';
2034         },
2035
2036         ArrayPattern: function (expr, precedence, flags) {
2037             return this.ArrayExpression(expr, precedence, flags, true);
2038         },
2039
2040         ArrayExpression: function (expr, precedence, flags, isPattern) {
2041             var result, multiline, that = this;
2042             if (!expr.elements.length) {
2043                 return '[]';
2044             }
2045             multiline = isPattern ? false : expr.elements.length > 1;
2046             result = ['[', multiline ? newline : ''];
2047             withIndent(function (indent) {
2048                 var i, iz;
2049                 for (i = 0, iz = expr.elements.length; i < iz; ++i) {
2050                     if (!expr.elements[i]) {
2051                         if (multiline) {
2052                             result.push(indent);
2053                         }
2054                         if (i + 1 === iz) {
2055                             result.push(',');
2056                         }
2057                     } else {
2058                         result.push(multiline ? indent : '');
2059                         result.push(that.generateExpression(expr.elements[i], Precedence.Assignment, E_TTT));
2060                     }
2061                     if (i + 1 < iz) {
2062                         result.push(',' + (multiline ? newline : space));
2063                     }
2064                 }
2065             });
2066             if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2067                 result.push(newline);
2068             }
2069             result.push(multiline ? base : '');
2070             result.push(']');
2071             return result;
2072         },
2073
2074         RestElement: function(expr, precedence, flags) {
2075             return '...' + this.generatePattern(expr.argument);
2076         },
2077
2078         ClassExpression: function (expr, precedence, flags) {
2079             var result, fragment;
2080             result = ['class'];
2081             if (expr.id) {
2082                 result = join(result, this.generateExpression(expr.id, Precedence.Sequence, E_TTT));
2083             }
2084             if (expr.superClass) {
2085                 fragment = join('extends', this.generateExpression(expr.superClass, Precedence.Assignment, E_TTT));
2086                 result = join(result, fragment);
2087             }
2088             result.push(space);
2089             result.push(this.generateStatement(expr.body, S_TFFT));
2090             return result;
2091         },
2092
2093         MethodDefinition: function (expr, precedence, flags) {
2094             var result, fragment;
2095             if (expr['static']) {
2096                 result = ['static' + space];
2097             } else {
2098                 result = [];
2099             }
2100             if (expr.kind === 'get' || expr.kind === 'set') {
2101                 fragment = [
2102                     join(expr.kind, this.generatePropertyKey(expr.key, expr.computed)),
2103                     this.generateFunctionBody(expr.value)
2104                 ];
2105             } else {
2106                 fragment = [
2107                     generateMethodPrefix(expr),
2108                     this.generatePropertyKey(expr.key, expr.computed),
2109                     this.generateFunctionBody(expr.value)
2110                 ];
2111             }
2112             return join(result, fragment);
2113         },
2114
2115         Property: function (expr, precedence, flags) {
2116             if (expr.kind === 'get' || expr.kind === 'set') {
2117                 return [
2118                     expr.kind, noEmptySpace(),
2119                     this.generatePropertyKey(expr.key, expr.computed),
2120                     this.generateFunctionBody(expr.value)
2121                 ];
2122             }
2123
2124             if (expr.shorthand) {
2125                 return this.generatePropertyKey(expr.key, expr.computed);
2126             }
2127
2128             if (expr.method) {
2129                 return [
2130                     generateMethodPrefix(expr),
2131                     this.generatePropertyKey(expr.key, expr.computed),
2132                     this.generateFunctionBody(expr.value)
2133                 ];
2134             }
2135
2136             return [
2137                 this.generatePropertyKey(expr.key, expr.computed),
2138                 ':' + space,
2139                 this.generateExpression(expr.value, Precedence.Assignment, E_TTT)
2140             ];
2141         },
2142
2143         ObjectExpression: function (expr, precedence, flags) {
2144             var multiline, result, fragment, that = this;
2145
2146             if (!expr.properties.length) {
2147                 return '{}';
2148             }
2149             multiline = expr.properties.length > 1;
2150
2151             withIndent(function () {
2152                 fragment = that.generateExpression(expr.properties[0], Precedence.Sequence, E_TTT);
2153             });
2154
2155             if (!multiline) {
2156                 // issues 4
2157                 // Do not transform from
2158                 //   dejavu.Class.declare({
2159                 //       method2: function () {}
2160                 //   });
2161                 // to
2162                 //   dejavu.Class.declare({method2: function () {
2163                 //       }});
2164                 if (!hasLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
2165                     return [ '{', space, fragment, space, '}' ];
2166                 }
2167             }
2168
2169             withIndent(function (indent) {
2170                 var i, iz;
2171                 result = [ '{', newline, indent, fragment ];
2172
2173                 if (multiline) {
2174                     result.push(',' + newline);
2175                     for (i = 1, iz = expr.properties.length; i < iz; ++i) {
2176                         result.push(indent);
2177                         result.push(that.generateExpression(expr.properties[i], Precedence.Sequence, E_TTT));
2178                         if (i + 1 < iz) {
2179                             result.push(',' + newline);
2180                         }
2181                     }
2182                 }
2183             });
2184
2185             if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2186                 result.push(newline);
2187             }
2188             result.push(base);
2189             result.push('}');
2190             return result;
2191         },
2192
2193         ObjectPattern: function (expr, precedence, flags) {
2194             var result, i, iz, multiline, property, that = this;
2195             if (!expr.properties.length) {
2196                 return '{}';
2197             }
2198
2199             multiline = false;
2200             if (expr.properties.length === 1) {
2201                 property = expr.properties[0];
2202                 if (property.value.type !== Syntax.Identifier) {
2203                     multiline = true;
2204                 }
2205             } else {
2206                 for (i = 0, iz = expr.properties.length; i < iz; ++i) {
2207                     property = expr.properties[i];
2208                     if (!property.shorthand) {
2209                         multiline = true;
2210                         break;
2211                     }
2212                 }
2213             }
2214             result = ['{', multiline ? newline : '' ];
2215
2216             withIndent(function (indent) {
2217                 var i, iz;
2218                 for (i = 0, iz = expr.properties.length; i < iz; ++i) {
2219                     result.push(multiline ? indent : '');
2220                     result.push(that.generateExpression(expr.properties[i], Precedence.Sequence, E_TTT));
2221                     if (i + 1 < iz) {
2222                         result.push(',' + (multiline ? newline : space));
2223                     }
2224                 }
2225             });
2226
2227             if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2228                 result.push(newline);
2229             }
2230             result.push(multiline ? base : '');
2231             result.push('}');
2232             return result;
2233         },
2234
2235         ThisExpression: function (expr, precedence, flags) {
2236             return 'this';
2237         },
2238
2239         Super: function (expr, precedence, flags) {
2240             return 'super';
2241         },
2242
2243         Identifier: function (expr, precedence, flags) {
2244             return generateIdentifier(expr);
2245         },
2246
2247         ImportDefaultSpecifier: function (expr, precedence, flags) {
2248             return generateIdentifier(expr.id || expr.local);
2249         },
2250
2251         ImportNamespaceSpecifier: function (expr, precedence, flags) {
2252             var result = ['*'];
2253             var id = expr.id || expr.local;
2254             if (id) {
2255                 result.push(space + 'as' + noEmptySpace() + generateIdentifier(id));
2256             }
2257             return result;
2258         },
2259
2260         ImportSpecifier: function (expr, precedence, flags) {
2261             return this.ExportSpecifier(expr, precedence, flags);
2262         },
2263
2264         ExportSpecifier: function (expr, precedence, flags) {
2265             var exported = (expr.id || expr.imported).name;
2266             var result = [ exported ];
2267             var id = expr.name || expr.local;
2268             if (id && id.name !== exported) {
2269                 result.push(noEmptySpace() + 'as' + noEmptySpace() + generateIdentifier(id));
2270             }
2271             return result;
2272         },
2273
2274         Literal: function (expr, precedence, flags) {
2275             var raw;
2276             if (expr.hasOwnProperty('raw') && parse && extra.raw) {
2277                 try {
2278                     raw = parse(expr.raw).body[0].expression;
2279                     if (raw.type === Syntax.Literal) {
2280                         if (raw.value === expr.value) {
2281                             return expr.raw;
2282                         }
2283                     }
2284                 } catch (e) {
2285                     // not use raw property
2286                 }
2287             }
2288
2289             if (expr.value === null) {
2290                 return 'null';
2291             }
2292
2293             if (typeof expr.value === 'string') {
2294                 return escapeString(expr.value);
2295             }
2296
2297             if (typeof expr.value === 'number') {
2298                 return generateNumber(expr.value);
2299             }
2300
2301             if (typeof expr.value === 'boolean') {
2302                 return expr.value ? 'true' : 'false';
2303             }
2304
2305             return generateRegExp(expr.value);
2306         },
2307
2308         GeneratorExpression: function (expr, precedence, flags) {
2309             return this.ComprehensionExpression(expr, precedence, flags);
2310         },
2311
2312         ComprehensionExpression: function (expr, precedence, flags) {
2313             // GeneratorExpression should be parenthesized with (...), ComprehensionExpression with [...]
2314             // Due to https://bugzilla.mozilla.org/show_bug.cgi?id=883468 position of expr.body can differ in Spidermonkey and ES6
2315
2316             var result, i, iz, fragment, that = this;
2317             result = (expr.type === Syntax.GeneratorExpression) ? ['('] : ['['];
2318
2319             if (extra.moz.comprehensionExpressionStartsWithAssignment) {
2320                 fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
2321                 result.push(fragment);
2322             }
2323
2324             if (expr.blocks) {
2325                 withIndent(function () {
2326                     for (i = 0, iz = expr.blocks.length; i < iz; ++i) {
2327                         fragment = that.generateExpression(expr.blocks[i], Precedence.Sequence, E_TTT);
2328                         if (i > 0 || extra.moz.comprehensionExpressionStartsWithAssignment) {
2329                             result = join(result, fragment);
2330                         } else {
2331                             result.push(fragment);
2332                         }
2333                     }
2334                 });
2335             }
2336
2337             if (expr.filter) {
2338                 result = join(result, 'if' + space);
2339                 fragment = this.generateExpression(expr.filter, Precedence.Sequence, E_TTT);
2340                 result = join(result, [ '(', fragment, ')' ]);
2341             }
2342
2343             if (!extra.moz.comprehensionExpressionStartsWithAssignment) {
2344                 fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
2345
2346                 result = join(result, fragment);
2347             }
2348
2349             result.push((expr.type === Syntax.GeneratorExpression) ? ')' : ']');
2350             return result;
2351         },
2352
2353         ComprehensionBlock: function (expr, precedence, flags) {
2354             var fragment;
2355             if (expr.left.type === Syntax.VariableDeclaration) {
2356                 fragment = [
2357                     expr.left.kind, noEmptySpace(),
2358                     this.generateStatement(expr.left.declarations[0], S_FFFF)
2359                 ];
2360             } else {
2361                 fragment = this.generateExpression(expr.left, Precedence.Call, E_TTT);
2362             }
2363
2364             fragment = join(fragment, expr.of ? 'of' : 'in');
2365             fragment = join(fragment, this.generateExpression(expr.right, Precedence.Sequence, E_TTT));
2366
2367             return [ 'for' + space + '(', fragment, ')' ];
2368         },
2369
2370         SpreadElement: function (expr, precedence, flags) {
2371             return [
2372                 '...',
2373                 this.generateExpression(expr.argument, Precedence.Assignment, E_TTT)
2374             ];
2375         },
2376
2377         TaggedTemplateExpression: function (expr, precedence, flags) {
2378             var itemFlags = E_TTF;
2379             if (!(flags & F_ALLOW_CALL)) {
2380                 itemFlags = E_TFF;
2381             }
2382             var result = [
2383                 this.generateExpression(expr.tag, Precedence.Call, itemFlags),
2384                 this.generateExpression(expr.quasi, Precedence.Primary, E_FFT)
2385             ];
2386             return parenthesize(result, Precedence.TaggedTemplate, precedence);
2387         },
2388
2389         TemplateElement: function (expr, precedence, flags) {
2390             // Don't use "cooked". Since tagged template can use raw template
2391             // representation. So if we do so, it breaks the script semantics.
2392             return expr.value.raw;
2393         },
2394
2395         TemplateLiteral: function (expr, precedence, flags) {
2396             var result, i, iz;
2397             result = [ '`' ];
2398             for (i = 0, iz = expr.quasis.length; i < iz; ++i) {
2399                 result.push(this.generateExpression(expr.quasis[i], Precedence.Primary, E_TTT));
2400                 if (i + 1 < iz) {
2401                     result.push('${' + space);
2402                     result.push(this.generateExpression(expr.expressions[i], Precedence.Sequence, E_TTT));
2403                     result.push(space + '}');
2404                 }
2405             }
2406             result.push('`');
2407             return result;
2408         },
2409
2410         ModuleSpecifier: function (expr, precedence, flags) {
2411             return this.Literal(expr, precedence, flags);
2412         }
2413
2414     };
2415
2416     merge(CodeGenerator.prototype, CodeGenerator.Expression);
2417
2418     CodeGenerator.prototype.generateExpression = function (expr, precedence, flags) {
2419         var result, type;
2420
2421         type = expr.type || Syntax.Property;
2422
2423         if (extra.verbatim && expr.hasOwnProperty(extra.verbatim)) {
2424             return generateVerbatim(expr, precedence);
2425         }
2426
2427         result = this[type](expr, precedence, flags);
2428
2429
2430         if (extra.comment) {
2431             result = addComments(expr, result);
2432         }
2433         return toSourceNodeWhenNeeded(result, expr);
2434     };
2435
2436     CodeGenerator.prototype.generateStatement = function (stmt, flags) {
2437         var result,
2438             fragment;
2439
2440         result = this[stmt.type](stmt, flags);
2441
2442         // Attach comments
2443
2444         if (extra.comment) {
2445             result = addComments(stmt, result);
2446         }
2447
2448         fragment = toSourceNodeWhenNeeded(result).toString();
2449         if (stmt.type === Syntax.Program && !safeConcatenation && newline === '' &&  fragment.charAt(fragment.length - 1) === '\n') {
2450             result = sourceMap ? toSourceNodeWhenNeeded(result).replaceRight(/\s+$/, '') : fragment.replace(/\s+$/, '');
2451         }
2452
2453         return toSourceNodeWhenNeeded(result, stmt);
2454     };
2455
2456     function generateInternal(node) {
2457         var codegen;
2458
2459         codegen = new CodeGenerator();
2460         if (isStatement(node)) {
2461             return codegen.generateStatement(node, S_TFFF);
2462         }
2463
2464         if (isExpression(node)) {
2465             return codegen.generateExpression(node, Precedence.Sequence, E_TTT);
2466         }
2467
2468         throw new Error('Unknown node type: ' + node.type);
2469     }
2470
2471     function generate(node, options) {
2472         var defaultOptions = getDefaultOptions(), result, pair;
2473
2474         if (options != null) {
2475             // Obsolete options
2476             //
2477             //   `options.indent`
2478             //   `options.base`
2479             //
2480             // Instead of them, we can use `option.format.indent`.
2481             if (typeof options.indent === 'string') {
2482                 defaultOptions.format.indent.style = options.indent;
2483             }
2484             if (typeof options.base === 'number') {
2485                 defaultOptions.format.indent.base = options.base;
2486             }
2487             options = updateDeeply(defaultOptions, options);
2488             indent = options.format.indent.style;
2489             if (typeof options.base === 'string') {
2490                 base = options.base;
2491             } else {
2492                 base = stringRepeat(indent, options.format.indent.base);
2493             }
2494         } else {
2495             options = defaultOptions;
2496             indent = options.format.indent.style;
2497             base = stringRepeat(indent, options.format.indent.base);
2498         }
2499         json = options.format.json;
2500         renumber = options.format.renumber;
2501         hexadecimal = json ? false : options.format.hexadecimal;
2502         quotes = json ? 'double' : options.format.quotes;
2503         escapeless = options.format.escapeless;
2504         newline = options.format.newline;
2505         space = options.format.space;
2506         if (options.format.compact) {
2507             newline = space = indent = base = '';
2508         }
2509         parentheses = options.format.parentheses;
2510         semicolons = options.format.semicolons;
2511         safeConcatenation = options.format.safeConcatenation;
2512         directive = options.directive;
2513         parse = json ? null : options.parse;
2514         sourceMap = options.sourceMap;
2515         sourceCode = options.sourceCode;
2516         preserveBlankLines = options.format.preserveBlankLines && sourceCode !== null;
2517         extra = options;
2518
2519         if (sourceMap) {
2520             if (!exports.browser) {
2521                 // We assume environment is node.js
2522                 // And prevent from including source-map by browserify
2523                 SourceNode = require('source-map').SourceNode;
2524             } else {
2525                 SourceNode = global.sourceMap.SourceNode;
2526             }
2527         }
2528
2529         result = generateInternal(node);
2530
2531         if (!sourceMap) {
2532             pair = {code: result.toString(), map: null};
2533             return options.sourceMapWithCode ? pair : pair.code;
2534         }
2535
2536
2537         pair = result.toStringWithSourceMap({
2538             file: options.file,
2539             sourceRoot: options.sourceMapRoot
2540         });
2541
2542         if (options.sourceContent) {
2543             pair.map.setSourceContent(options.sourceMap,
2544                                       options.sourceContent);
2545         }
2546
2547         if (options.sourceMapWithCode) {
2548             return pair;
2549         }
2550
2551         return pair.map.toString();
2552     }
2553
2554     FORMAT_MINIFY = {
2555         indent: {
2556             style: '',
2557             base: 0
2558         },
2559         renumber: true,
2560         hexadecimal: true,
2561         quotes: 'auto',
2562         escapeless: true,
2563         compact: true,
2564         parentheses: false,
2565         semicolons: false
2566     };
2567
2568     FORMAT_DEFAULTS = getDefaultOptions().format;
2569
2570     exports.version = require('./package.json').version;
2571     exports.generate = generate;
2572     exports.attachComments = estraverse.attachComments;
2573     exports.Precedence = updateDeeply({}, Precedence);
2574     exports.browser = false;
2575     exports.FORMAT_MINIFY = FORMAT_MINIFY;
2576     exports.FORMAT_DEFAULTS = FORMAT_DEFAULTS;
2577 }());
2578 /* vim: set sw=4 ts=4 et tw=80 : */