UNPKG

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