UNPKG

92.8 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 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 ((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.isIdentifierPart(leftCharCode) && esutils.code.isIdentifierPart(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 ExpressionStatement: function (stmt, flags) {
1269 var result, fragment;
1270
1271 function isClassPrefixed(fragment) {
1272 var code;
1273 if (fragment.slice(0, 5) !== 'class') {
1274 return false;
1275 }
1276 code = fragment.charCodeAt(5);
1277 return code === 0x7B /* '{' */ || esutils.code.isWhiteSpace(code) || esutils.code.isLineTerminator(code);
1278 }
1279
1280 function isFunctionPrefixed(fragment) {
1281 var code;
1282 if (fragment.slice(0, 8) !== 'function') {
1283 return false;
1284 }
1285 code = fragment.charCodeAt(8);
1286 return code === 0x28 /* '(' */ || esutils.code.isWhiteSpace(code) || code === 0x2A /* '*' */ || esutils.code.isLineTerminator(code);
1287 }
1288
1289 function isAsyncPrefixed(fragment) {
1290 var code, i, iz;
1291 if (fragment.slice(0, 5) !== 'async') {
1292 return false;
1293 }
1294 if (!esutils.code.isWhiteSpace(fragment.charCodeAt(5))) {
1295 return false;
1296 }
1297 for (i = 6, iz = fragment.length; i < iz; ++i) {
1298 if (!esutils.code.isWhiteSpace(fragment.charCodeAt(i))) {
1299 break;
1300 }
1301 }
1302 if (i === iz) {
1303 return false;
1304 }
1305 if (fragment.slice(i, i + 8) !== 'function') {
1306 return false;
1307 }
1308 code = fragment.charCodeAt(i + 8);
1309 return code === 0x28 /* '(' */ || esutils.code.isWhiteSpace(code) || code === 0x2A /* '*' */ || esutils.code.isLineTerminator(code);
1310 }
1311
1312 result = [this.generateExpression(stmt.expression, Precedence.Sequence, E_TTT)];
1313 // 12.4 '{', 'function', 'class' is not allowed in this position.
1314 // wrap expression with parentheses
1315 fragment = toSourceNodeWhenNeeded(result).toString();
1316 if (fragment.charCodeAt(0) === 0x7B /* '{' */ || // ObjectExpression
1317 isClassPrefixed(fragment) ||
1318 isFunctionPrefixed(fragment) ||
1319 isAsyncPrefixed(fragment) ||
1320 (directive && (flags & F_DIRECTIVE_CTX) && stmt.expression.type === Syntax.Literal && typeof stmt.expression.value === 'string')) {
1321 result = ['(', result, ')' + this.semicolon(flags)];
1322 } else {
1323 result.push(this.semicolon(flags));
1324 }
1325 return result;
1326 },
1327
1328 ImportDeclaration: function (stmt, flags) {
1329 // ES6: 15.2.1 valid import declarations:
1330 // - import ImportClause FromClause ;
1331 // - import ModuleSpecifier ;
1332 var result, cursor, that = this;
1333
1334 // If no ImportClause is present,
1335 // this should be `import ModuleSpecifier` so skip `from`
1336 // ModuleSpecifier is StringLiteral.
1337 if (stmt.specifiers.length === 0) {
1338 // import ModuleSpecifier ;
1339 return [
1340 'import',
1341 space,
1342 // ModuleSpecifier
1343 this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1344 this.semicolon(flags)
1345 ];
1346 }
1347
1348 // import ImportClause FromClause ;
1349 result = [
1350 'import'
1351 ];
1352 cursor = 0;
1353
1354 // ImportedBinding
1355 if (stmt.specifiers[cursor].type === Syntax.ImportDefaultSpecifier) {
1356 result = join(result, [
1357 this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
1358 ]);
1359 ++cursor;
1360 }
1361
1362 if (stmt.specifiers[cursor]) {
1363 if (cursor !== 0) {
1364 result.push(',');
1365 }
1366
1367 if (stmt.specifiers[cursor].type === Syntax.ImportNamespaceSpecifier) {
1368 // NameSpaceImport
1369 result = join(result, [
1370 space,
1371 this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
1372 ]);
1373 } else {
1374 // NamedImports
1375 result.push(space + '{');
1376
1377 if ((stmt.specifiers.length - cursor) === 1) {
1378 // import { ... } from "...";
1379 result.push(space);
1380 result.push(this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT));
1381 result.push(space + '}' + space);
1382 } else {
1383 // import {
1384 // ...,
1385 // ...,
1386 // } from "...";
1387 withIndent(function (indent) {
1388 var i, iz;
1389 result.push(newline);
1390 for (i = cursor, iz = stmt.specifiers.length; i < iz; ++i) {
1391 result.push(indent);
1392 result.push(that.generateExpression(stmt.specifiers[i], Precedence.Sequence, E_TTT));
1393 if (i + 1 < iz) {
1394 result.push(',' + newline);
1395 }
1396 }
1397 });
1398 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1399 result.push(newline);
1400 }
1401 result.push(base + '}' + space);
1402 }
1403 }
1404 }
1405
1406 result = join(result, [
1407 'from' + space,
1408 // ModuleSpecifier
1409 this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1410 this.semicolon(flags)
1411 ]);
1412 return result;
1413 },
1414
1415 VariableDeclarator: function (stmt, flags) {
1416 var itemFlags = (flags & F_ALLOW_IN) ? E_TTT : E_FTT;
1417 if (stmt.init) {
1418 return [
1419 this.generateExpression(stmt.id, Precedence.Assignment, itemFlags),
1420 space,
1421 '=',
1422 space,
1423 this.generateExpression(stmt.init, Precedence.Assignment, itemFlags)
1424 ];
1425 }
1426 return this.generatePattern(stmt.id, Precedence.Assignment, itemFlags);
1427 },
1428
1429 VariableDeclaration: function (stmt, flags) {
1430 // VariableDeclarator is typed as Statement,
1431 // but joined with comma (not LineTerminator).
1432 // So if comment is attached to target node, we should specialize.
1433 var result, i, iz, node, bodyFlags, that = this;
1434
1435 result = [ stmt.kind ];
1436
1437 bodyFlags = (flags & F_ALLOW_IN) ? S_TFFF : S_FFFF;
1438
1439 function block() {
1440 node = stmt.declarations[0];
1441 if (extra.comment && node.leadingComments) {
1442 result.push('\n');
1443 result.push(addIndent(that.generateStatement(node, bodyFlags)));
1444 } else {
1445 result.push(noEmptySpace());
1446 result.push(that.generateStatement(node, bodyFlags));
1447 }
1448
1449 for (i = 1, iz = stmt.declarations.length; i < iz; ++i) {
1450 node = stmt.declarations[i];
1451 if (extra.comment && node.leadingComments) {
1452 result.push(',' + newline);
1453 result.push(addIndent(that.generateStatement(node, bodyFlags)));
1454 } else {
1455 result.push(',' + space);
1456 result.push(that.generateStatement(node, bodyFlags));
1457 }
1458 }
1459 }
1460
1461 if (stmt.declarations.length > 1) {
1462 withIndent(block);
1463 } else {
1464 block();
1465 }
1466
1467 result.push(this.semicolon(flags));
1468
1469 return result;
1470 },
1471
1472 ThrowStatement: function (stmt, flags) {
1473 return [join(
1474 'throw',
1475 this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
1476 ), this.semicolon(flags)];
1477 },
1478
1479 TryStatement: function (stmt, flags) {
1480 var result, i, iz, guardedHandlers;
1481
1482 result = ['try', this.maybeBlock(stmt.block, S_TFFF)];
1483 result = this.maybeBlockSuffix(stmt.block, result);
1484
1485 if (stmt.handlers) {
1486 // old interface
1487 for (i = 0, iz = stmt.handlers.length; i < iz; ++i) {
1488 result = join(result, this.generateStatement(stmt.handlers[i], S_TFFF));
1489 if (stmt.finalizer || i + 1 !== iz) {
1490 result = this.maybeBlockSuffix(stmt.handlers[i].body, result);
1491 }
1492 }
1493 } else {
1494 guardedHandlers = stmt.guardedHandlers || [];
1495
1496 for (i = 0, iz = guardedHandlers.length; i < iz; ++i) {
1497 result = join(result, this.generateStatement(guardedHandlers[i], S_TFFF));
1498 if (stmt.finalizer || i + 1 !== iz) {
1499 result = this.maybeBlockSuffix(guardedHandlers[i].body, result);
1500 }
1501 }
1502
1503 // new interface
1504 if (stmt.handler) {
1505 if (isArray(stmt.handler)) {
1506 for (i = 0, iz = stmt.handler.length; i < iz; ++i) {
1507 result = join(result, this.generateStatement(stmt.handler[i], S_TFFF));
1508 if (stmt.finalizer || i + 1 !== iz) {
1509 result = this.maybeBlockSuffix(stmt.handler[i].body, result);
1510 }
1511 }
1512 } else {
1513 result = join(result, this.generateStatement(stmt.handler, S_TFFF));
1514 if (stmt.finalizer) {
1515 result = this.maybeBlockSuffix(stmt.handler.body, result);
1516 }
1517 }
1518 }
1519 }
1520 if (stmt.finalizer) {
1521 result = join(result, ['finally', this.maybeBlock(stmt.finalizer, S_TFFF)]);
1522 }
1523 return result;
1524 },
1525
1526 SwitchStatement: function (stmt, flags) {
1527 var result, fragment, i, iz, bodyFlags, that = this;
1528 withIndent(function () {
1529 result = [
1530 'switch' + space + '(',
1531 that.generateExpression(stmt.discriminant, Precedence.Sequence, E_TTT),
1532 ')' + space + '{' + newline
1533 ];
1534 });
1535 if (stmt.cases) {
1536 bodyFlags = S_TFFF;
1537 for (i = 0, iz = stmt.cases.length; i < iz; ++i) {
1538 if (i === iz - 1) {
1539 bodyFlags |= F_SEMICOLON_OPT;
1540 }
1541 fragment = addIndent(this.generateStatement(stmt.cases[i], bodyFlags));
1542 result.push(fragment);
1543 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1544 result.push(newline);
1545 }
1546 }
1547 }
1548 result.push(addIndent('}'));
1549 return result;
1550 },
1551
1552 SwitchCase: function (stmt, flags) {
1553 var result, fragment, i, iz, bodyFlags, that = this;
1554 withIndent(function () {
1555 if (stmt.test) {
1556 result = [
1557 join('case', that.generateExpression(stmt.test, Precedence.Sequence, E_TTT)),
1558 ':'
1559 ];
1560 } else {
1561 result = ['default:'];
1562 }
1563
1564 i = 0;
1565 iz = stmt.consequent.length;
1566 if (iz && stmt.consequent[0].type === Syntax.BlockStatement) {
1567 fragment = that.maybeBlock(stmt.consequent[0], S_TFFF);
1568 result.push(fragment);
1569 i = 1;
1570 }
1571
1572 if (i !== iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1573 result.push(newline);
1574 }
1575
1576 bodyFlags = S_TFFF;
1577 for (; i < iz; ++i) {
1578 if (i === iz - 1 && flags & F_SEMICOLON_OPT) {
1579 bodyFlags |= F_SEMICOLON_OPT;
1580 }
1581 fragment = addIndent(that.generateStatement(stmt.consequent[i], bodyFlags));
1582 result.push(fragment);
1583 if (i + 1 !== iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1584 result.push(newline);
1585 }
1586 }
1587 });
1588 return result;
1589 },
1590
1591 IfStatement: function (stmt, flags) {
1592 var result, bodyFlags, semicolonOptional, that = this;
1593 withIndent(function () {
1594 result = [
1595 'if' + space + '(',
1596 that.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1597 ')'
1598 ];
1599 });
1600 semicolonOptional = flags & F_SEMICOLON_OPT;
1601 bodyFlags = S_TFFF;
1602 if (semicolonOptional) {
1603 bodyFlags |= F_SEMICOLON_OPT;
1604 }
1605 if (stmt.alternate) {
1606 result.push(this.maybeBlock(stmt.consequent, S_TFFF));
1607 result = this.maybeBlockSuffix(stmt.consequent, result);
1608 if (stmt.alternate.type === Syntax.IfStatement) {
1609 result = join(result, ['else ', this.generateStatement(stmt.alternate, bodyFlags)]);
1610 } else {
1611 result = join(result, join('else', this.maybeBlock(stmt.alternate, bodyFlags)));
1612 }
1613 } else {
1614 result.push(this.maybeBlock(stmt.consequent, bodyFlags));
1615 }
1616 return result;
1617 },
1618
1619 ForStatement: function (stmt, flags) {
1620 var result, that = this;
1621 withIndent(function () {
1622 result = ['for' + space + '('];
1623 if (stmt.init) {
1624 if (stmt.init.type === Syntax.VariableDeclaration) {
1625 result.push(that.generateStatement(stmt.init, S_FFFF));
1626 } else {
1627 // F_ALLOW_IN becomes false.
1628 result.push(that.generateExpression(stmt.init, Precedence.Sequence, E_FTT));
1629 result.push(';');
1630 }
1631 } else {
1632 result.push(';');
1633 }
1634
1635 if (stmt.test) {
1636 result.push(space);
1637 result.push(that.generateExpression(stmt.test, Precedence.Sequence, E_TTT));
1638 result.push(';');
1639 } else {
1640 result.push(';');
1641 }
1642
1643 if (stmt.update) {
1644 result.push(space);
1645 result.push(that.generateExpression(stmt.update, Precedence.Sequence, E_TTT));
1646 result.push(')');
1647 } else {
1648 result.push(')');
1649 }
1650 });
1651
1652 result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1653 return result;
1654 },
1655
1656 ForInStatement: function (stmt, flags) {
1657 return this.generateIterationForStatement('in', stmt, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF);
1658 },
1659
1660 ForOfStatement: function (stmt, flags) {
1661 return this.generateIterationForStatement('of', stmt, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF);
1662 },
1663
1664 LabeledStatement: function (stmt, flags) {
1665 return [stmt.label.name + ':', this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF)];
1666 },
1667
1668 Program: function (stmt, flags) {
1669 var result, fragment, i, iz, bodyFlags;
1670 iz = stmt.body.length;
1671 result = [safeConcatenation && iz > 0 ? '\n' : ''];
1672 bodyFlags = S_TFTF;
1673 for (i = 0; i < iz; ++i) {
1674 if (!safeConcatenation && i === iz - 1) {
1675 bodyFlags |= F_SEMICOLON_OPT;
1676 }
1677
1678 if (preserveBlankLines) {
1679 // handle spaces before the first line
1680 if (i === 0) {
1681 if (!stmt.body[0].leadingComments) {
1682 generateBlankLines(stmt.range[0], stmt.body[i].range[0], result);
1683 }
1684 }
1685
1686 // handle spaces between lines
1687 if (i > 0) {
1688 if (!stmt.body[i - 1].trailingComments && !stmt.body[i].leadingComments) {
1689 generateBlankLines(stmt.body[i - 1].range[1], stmt.body[i].range[0], result);
1690 }
1691 }
1692 }
1693
1694 fragment = addIndent(this.generateStatement(stmt.body[i], bodyFlags));
1695 result.push(fragment);
1696 if (i + 1 < iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1697 if (preserveBlankLines) {
1698 if (!stmt.body[i + 1].leadingComments) {
1699 result.push(newline);
1700 }
1701 } else {
1702 result.push(newline);
1703 }
1704 }
1705
1706 if (preserveBlankLines) {
1707 // handle spaces after the last line
1708 if (i === iz - 1) {
1709 if (!stmt.body[i].trailingComments) {
1710 generateBlankLines(stmt.body[i].range[1], stmt.range[1], result);
1711 }
1712 }
1713 }
1714 }
1715 return result;
1716 },
1717
1718 FunctionDeclaration: function (stmt, flags) {
1719 return [
1720 generateAsyncPrefix(stmt, true),
1721 'function',
1722 generateStarSuffix(stmt) || noEmptySpace(),
1723 generateIdentifier(stmt.id),
1724 this.generateFunctionBody(stmt)
1725 ];
1726 },
1727
1728 ReturnStatement: function (stmt, flags) {
1729 if (stmt.argument) {
1730 return [join(
1731 'return',
1732 this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
1733 ), this.semicolon(flags)];
1734 }
1735 return ['return' + this.semicolon(flags)];
1736 },
1737
1738 WhileStatement: function (stmt, flags) {
1739 var result, that = this;
1740 withIndent(function () {
1741 result = [
1742 'while' + space + '(',
1743 that.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1744 ')'
1745 ];
1746 });
1747 result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1748 return result;
1749 },
1750
1751 WithStatement: function (stmt, flags) {
1752 var result, that = this;
1753 withIndent(function () {
1754 result = [
1755 'with' + space + '(',
1756 that.generateExpression(stmt.object, Precedence.Sequence, E_TTT),
1757 ')'
1758 ];
1759 });
1760 result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1761 return result;
1762 }
1763
1764 };
1765
1766 merge(CodeGenerator.prototype, CodeGenerator.Statement);
1767
1768 // Expressions.
1769
1770 CodeGenerator.Expression = {
1771
1772 SequenceExpression: function (expr, precedence, flags) {
1773 var result, i, iz;
1774 if (Precedence.Sequence < precedence) {
1775 flags |= F_ALLOW_IN;
1776 }
1777 result = [];
1778 for (i = 0, iz = expr.expressions.length; i < iz; ++i) {
1779 result.push(this.generateExpression(expr.expressions[i], Precedence.Assignment, flags));
1780 if (i + 1 < iz) {
1781 result.push(',' + space);
1782 }
1783 }
1784 return parenthesize(result, Precedence.Sequence, precedence);
1785 },
1786
1787 AssignmentExpression: function (expr, precedence, flags) {
1788 return this.generateAssignment(expr.left, expr.right, expr.operator, precedence, flags);
1789 },
1790
1791 ArrowFunctionExpression: function (expr, precedence, flags) {
1792 return parenthesize(this.generateFunctionBody(expr), Precedence.ArrowFunction, precedence);
1793 },
1794
1795 ConditionalExpression: function (expr, precedence, flags) {
1796 if (Precedence.Conditional < precedence) {
1797 flags |= F_ALLOW_IN;
1798 }
1799 return parenthesize(
1800 [
1801 this.generateExpression(expr.test, Precedence.LogicalOR, flags),
1802 space + '?' + space,
1803 this.generateExpression(expr.consequent, Precedence.Assignment, flags),
1804 space + ':' + space,
1805 this.generateExpression(expr.alternate, Precedence.Assignment, flags)
1806 ],
1807 Precedence.Conditional,
1808 precedence
1809 );
1810 },
1811
1812 LogicalExpression: function (expr, precedence, flags) {
1813 return this.BinaryExpression(expr, precedence, flags);
1814 },
1815
1816 BinaryExpression: function (expr, precedence, flags) {
1817 var result, currentPrecedence, fragment, leftSource;
1818 currentPrecedence = BinaryPrecedence[expr.operator];
1819
1820 if (currentPrecedence < precedence) {
1821 flags |= F_ALLOW_IN;
1822 }
1823
1824 fragment = this.generateExpression(expr.left, currentPrecedence, flags);
1825
1826 leftSource = fragment.toString();
1827
1828 if (leftSource.charCodeAt(leftSource.length - 1) === 0x2F /* / */ && esutils.code.isIdentifierPart(expr.operator.charCodeAt(0))) {
1829 result = [fragment, noEmptySpace(), expr.operator];
1830 } else {
1831 result = join(fragment, expr.operator);
1832 }
1833
1834 fragment = this.generateExpression(expr.right, currentPrecedence + 1, flags);
1835
1836 if (expr.operator === '/' && fragment.toString().charAt(0) === '/' ||
1837 expr.operator.slice(-1) === '<' && fragment.toString().slice(0, 3) === '!--') {
1838 // If '/' concats with '/' or `<` concats with `!--`, it is interpreted as comment start
1839 result.push(noEmptySpace());
1840 result.push(fragment);
1841 } else {
1842 result = join(result, fragment);
1843 }
1844
1845 if (expr.operator === 'in' && !(flags & F_ALLOW_IN)) {
1846 return ['(', result, ')'];
1847 }
1848 return parenthesize(result, currentPrecedence, precedence);
1849 },
1850
1851 CallExpression: function (expr, precedence, flags) {
1852 var result, i, iz;
1853 // F_ALLOW_UNPARATH_NEW becomes false.
1854 result = [this.generateExpression(expr.callee, Precedence.Call, E_TTF)];
1855 result.push('(');
1856 for (i = 0, iz = expr['arguments'].length; i < iz; ++i) {
1857 result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
1858 if (i + 1 < iz) {
1859 result.push(',' + space);
1860 }
1861 }
1862 result.push(')');
1863
1864 if (!(flags & F_ALLOW_CALL)) {
1865 return ['(', result, ')'];
1866 }
1867 return parenthesize(result, Precedence.Call, precedence);
1868 },
1869
1870 NewExpression: function (expr, precedence, flags) {
1871 var result, length, i, iz, itemFlags;
1872 length = expr['arguments'].length;
1873
1874 // F_ALLOW_CALL becomes false.
1875 // F_ALLOW_UNPARATH_NEW may become false.
1876 itemFlags = (flags & F_ALLOW_UNPARATH_NEW && !parentheses && length === 0) ? E_TFT : E_TFF;
1877
1878 result = join(
1879 'new',
1880 this.generateExpression(expr.callee, Precedence.New, itemFlags)
1881 );
1882
1883 if (!(flags & F_ALLOW_UNPARATH_NEW) || parentheses || length > 0) {
1884 result.push('(');
1885 for (i = 0, iz = length; i < iz; ++i) {
1886 result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
1887 if (i + 1 < iz) {
1888 result.push(',' + space);
1889 }
1890 }
1891 result.push(')');
1892 }
1893
1894 return parenthesize(result, Precedence.New, precedence);
1895 },
1896
1897 MemberExpression: function (expr, precedence, flags) {
1898 var result, fragment;
1899
1900 // F_ALLOW_UNPARATH_NEW becomes false.
1901 result = [this.generateExpression(expr.object, Precedence.Call, (flags & F_ALLOW_CALL) ? E_TTF : E_TFF)];
1902
1903 if (expr.computed) {
1904 result.push('[');
1905 result.push(this.generateExpression(expr.property, Precedence.Sequence, flags & F_ALLOW_CALL ? E_TTT : E_TFT));
1906 result.push(']');
1907 } else {
1908 if (expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') {
1909 fragment = toSourceNodeWhenNeeded(result).toString();
1910 // When the following conditions are all true,
1911 // 1. No floating point
1912 // 2. Don't have exponents
1913 // 3. The last character is a decimal digit
1914 // 4. Not hexadecimal OR octal number literal
1915 // we should add a floating point.
1916 if (
1917 fragment.indexOf('.') < 0 &&
1918 !/[eExX]/.test(fragment) &&
1919 esutils.code.isDecimalDigit(fragment.charCodeAt(fragment.length - 1)) &&
1920 !(fragment.length >= 2 && fragment.charCodeAt(0) === 48) // '0'
1921 ) {
1922 result.push('.');
1923 }
1924 }
1925 result.push('.');
1926 result.push(generateIdentifier(expr.property));
1927 }
1928
1929 return parenthesize(result, Precedence.Member, precedence);
1930 },
1931
1932 UnaryExpression: function (expr, precedence, flags) {
1933 var result, fragment, rightCharCode, leftSource, leftCharCode;
1934 fragment = this.generateExpression(expr.argument, Precedence.Unary, E_TTT);
1935
1936 if (space === '') {
1937 result = join(expr.operator, fragment);
1938 } else {
1939 result = [expr.operator];
1940 if (expr.operator.length > 2) {
1941 // delete, void, typeof
1942 // get `typeof []`, not `typeof[]`
1943 result = join(result, fragment);
1944 } else {
1945 // Prevent inserting spaces between operator and argument if it is unnecessary
1946 // like, `!cond`
1947 leftSource = toSourceNodeWhenNeeded(result).toString();
1948 leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
1949 rightCharCode = fragment.toString().charCodeAt(0);
1950
1951 if (((leftCharCode === 0x2B /* + */ || leftCharCode === 0x2D /* - */) && leftCharCode === rightCharCode) ||
1952 (esutils.code.isIdentifierPart(leftCharCode) && esutils.code.isIdentifierPart(rightCharCode))) {
1953 result.push(noEmptySpace());
1954 result.push(fragment);
1955 } else {
1956 result.push(fragment);
1957 }
1958 }
1959 }
1960 return parenthesize(result, Precedence.Unary, precedence);
1961 },
1962
1963 YieldExpression: function (expr, precedence, flags) {
1964 var result;
1965 if (expr.delegate) {
1966 result = 'yield*';
1967 } else {
1968 result = 'yield';
1969 }
1970 if (expr.argument) {
1971 result = join(
1972 result,
1973 this.generateExpression(expr.argument, Precedence.Yield, E_TTT)
1974 );
1975 }
1976 return parenthesize(result, Precedence.Yield, precedence);
1977 },
1978
1979 AwaitExpression: function (expr, precedence, flags) {
1980 var result = join(
1981 expr.delegate ? 'await*' : 'await',
1982 this.generateExpression(expr.argument, Precedence.Await, E_TTT)
1983 );
1984 return parenthesize(result, Precedence.Await, precedence);
1985 },
1986
1987 UpdateExpression: function (expr, precedence, flags) {
1988 if (expr.prefix) {
1989 return parenthesize(
1990 [
1991 expr.operator,
1992 this.generateExpression(expr.argument, Precedence.Unary, E_TTT)
1993 ],
1994 Precedence.Unary,
1995 precedence
1996 );
1997 }
1998 return parenthesize(
1999 [
2000 this.generateExpression(expr.argument, Precedence.Postfix, E_TTT),
2001 expr.operator
2002 ],
2003 Precedence.Postfix,
2004 precedence
2005 );
2006 },
2007
2008 FunctionExpression: function (expr, precedence, flags) {
2009 var result = [
2010 generateAsyncPrefix(expr, true),
2011 'function'
2012 ];
2013 if (expr.id) {
2014 result.push(generateStarSuffix(expr) || noEmptySpace());
2015 result.push(generateIdentifier(expr.id));
2016 } else {
2017 result.push(generateStarSuffix(expr) || space);
2018 }
2019 result.push(this.generateFunctionBody(expr));
2020 return result;
2021 },
2022
2023 ExportBatchSpecifier: function (expr, precedence, flags) {
2024 return '*';
2025 },
2026
2027 ArrayPattern: function (expr, precedence, flags) {
2028 return this.ArrayExpression(expr, precedence, flags);
2029 },
2030
2031 ArrayExpression: function (expr, precedence, flags) {
2032 var result, multiline, that = this;
2033 if (!expr.elements.length) {
2034 return '[]';
2035 }
2036 multiline = expr.elements.length > 1;
2037 result = ['[', multiline ? newline : ''];
2038 withIndent(function (indent) {
2039 var i, iz;
2040 for (i = 0, iz = expr.elements.length; i < iz; ++i) {
2041 if (!expr.elements[i]) {
2042 if (multiline) {
2043 result.push(indent);
2044 }
2045 if (i + 1 === iz) {
2046 result.push(',');
2047 }
2048 } else {
2049 result.push(multiline ? indent : '');
2050 result.push(that.generateExpression(expr.elements[i], Precedence.Assignment, E_TTT));
2051 }
2052 if (i + 1 < iz) {
2053 result.push(',' + (multiline ? newline : space));
2054 }
2055 }
2056 });
2057 if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2058 result.push(newline);
2059 }
2060 result.push(multiline ? base : '');
2061 result.push(']');
2062 return result;
2063 },
2064
2065 ClassExpression: function (expr, precedence, flags) {
2066 var result, fragment;
2067 result = ['class'];
2068 if (expr.id) {
2069 result = join(result, this.generateExpression(expr.id, Precedence.Sequence, E_TTT));
2070 }
2071 if (expr.superClass) {
2072 fragment = join('extends', this.generateExpression(expr.superClass, Precedence.Assignment, E_TTT));
2073 result = join(result, fragment);
2074 }
2075 result.push(space);
2076 result.push(this.generateStatement(expr.body, S_TFFT));
2077 return result;
2078 },
2079
2080 MethodDefinition: function (expr, precedence, flags) {
2081 var result, fragment;
2082 if (expr['static']) {
2083 result = ['static' + space];
2084 } else {
2085 result = [];
2086 }
2087 if (expr.kind === 'get' || expr.kind === 'set') {
2088 fragment = [
2089 join(expr.kind, this.generatePropertyKey(expr.key, expr.computed)),
2090 this.generateFunctionBody(expr.value)
2091 ];
2092 } else {
2093 fragment = [
2094 generateMethodPrefix(expr),
2095 this.generatePropertyKey(expr.key, expr.computed),
2096 this.generateFunctionBody(expr.value)
2097 ];
2098 }
2099 return join(result, fragment);
2100 },
2101
2102 Property: function (expr, precedence, flags) {
2103 if (expr.kind === 'get' || expr.kind === 'set') {
2104 return [
2105 expr.kind, noEmptySpace(),
2106 this.generatePropertyKey(expr.key, expr.computed),
2107 this.generateFunctionBody(expr.value)
2108 ];
2109 }
2110
2111 if (expr.shorthand) {
2112 return this.generatePropertyKey(expr.key, expr.computed);
2113 }
2114
2115 if (expr.method) {
2116 return [
2117 generateMethodPrefix(expr),
2118 this.generatePropertyKey(expr.key, expr.computed),
2119 this.generateFunctionBody(expr.value)
2120 ];
2121 }
2122
2123 return [
2124 this.generatePropertyKey(expr.key, expr.computed),
2125 ':' + space,
2126 this.generateExpression(expr.value, Precedence.Assignment, E_TTT)
2127 ];
2128 },
2129
2130 ObjectExpression: function (expr, precedence, flags) {
2131 var multiline, result, fragment, that = this;
2132
2133 if (!expr.properties.length) {
2134 return '{}';
2135 }
2136 multiline = expr.properties.length > 1;
2137
2138 withIndent(function () {
2139 fragment = that.generateExpression(expr.properties[0], Precedence.Sequence, E_TTT);
2140 });
2141
2142 if (!multiline) {
2143 // issues 4
2144 // Do not transform from
2145 // dejavu.Class.declare({
2146 // method2: function () {}
2147 // });
2148 // to
2149 // dejavu.Class.declare({method2: function () {
2150 // }});
2151 if (!hasLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
2152 return [ '{', space, fragment, space, '}' ];
2153 }
2154 }
2155
2156 withIndent(function (indent) {
2157 var i, iz;
2158 result = [ '{', newline, indent, fragment ];
2159
2160 if (multiline) {
2161 result.push(',' + newline);
2162 for (i = 1, iz = expr.properties.length; i < iz; ++i) {
2163 result.push(indent);
2164 result.push(that.generateExpression(expr.properties[i], Precedence.Sequence, E_TTT));
2165 if (i + 1 < iz) {
2166 result.push(',' + newline);
2167 }
2168 }
2169 }
2170 });
2171
2172 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2173 result.push(newline);
2174 }
2175 result.push(base);
2176 result.push('}');
2177 return result;
2178 },
2179
2180 ObjectPattern: function (expr, precedence, flags) {
2181 var result, i, iz, multiline, property, that = this;
2182 if (!expr.properties.length) {
2183 return '{}';
2184 }
2185
2186 multiline = false;
2187 if (expr.properties.length === 1) {
2188 property = expr.properties[0];
2189 if (property.value.type !== Syntax.Identifier) {
2190 multiline = true;
2191 }
2192 } else {
2193 for (i = 0, iz = expr.properties.length; i < iz; ++i) {
2194 property = expr.properties[i];
2195 if (!property.shorthand) {
2196 multiline = true;
2197 break;
2198 }
2199 }
2200 }
2201 result = ['{', multiline ? newline : '' ];
2202
2203 withIndent(function (indent) {
2204 var i, iz;
2205 for (i = 0, iz = expr.properties.length; i < iz; ++i) {
2206 result.push(multiline ? indent : '');
2207 result.push(that.generateExpression(expr.properties[i], Precedence.Sequence, E_TTT));
2208 if (i + 1 < iz) {
2209 result.push(',' + (multiline ? newline : space));
2210 }
2211 }
2212 });
2213
2214 if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2215 result.push(newline);
2216 }
2217 result.push(multiline ? base : '');
2218 result.push('}');
2219 return result;
2220 },
2221
2222 ThisExpression: function (expr, precedence, flags) {
2223 return 'this';
2224 },
2225
2226 Identifier: function (expr, precedence, flags) {
2227 return generateIdentifier(expr);
2228 },
2229
2230 ImportDefaultSpecifier: function (expr, precedence, flags) {
2231 return generateIdentifier(expr.id);
2232 },
2233
2234 ImportNamespaceSpecifier: function (expr, precedence, flags) {
2235 var result = ['*'];
2236 if (expr.id) {
2237 result.push(space + 'as' + noEmptySpace() + generateIdentifier(expr.id));
2238 }
2239 return result;
2240 },
2241
2242 ImportSpecifier: function (expr, precedence, flags) {
2243 return this.ExportSpecifier(expr, precedence, flags);
2244 },
2245
2246 ExportSpecifier: function (expr, precedence, flags) {
2247 var result = [ expr.id.name ];
2248 if (expr.name) {
2249 result.push(noEmptySpace() + 'as' + noEmptySpace() + generateIdentifier(expr.name));
2250 }
2251 return result;
2252 },
2253
2254 Literal: function (expr, precedence, flags) {
2255 var raw;
2256 if (expr.hasOwnProperty('raw') && parse && extra.raw) {
2257 try {
2258 raw = parse(expr.raw).body[0].expression;
2259 if (raw.type === Syntax.Literal) {
2260 if (raw.value === expr.value) {
2261 return expr.raw;
2262 }
2263 }
2264 } catch (e) {
2265 // not use raw property
2266 }
2267 }
2268
2269 if (expr.value === null) {
2270 return 'null';
2271 }
2272
2273 if (typeof expr.value === 'string') {
2274 return escapeString(expr.value);
2275 }
2276
2277 if (typeof expr.value === 'number') {
2278 return generateNumber(expr.value);
2279 }
2280
2281 if (typeof expr.value === 'boolean') {
2282 return expr.value ? 'true' : 'false';
2283 }
2284
2285 return generateRegExp(expr.value);
2286 },
2287
2288 GeneratorExpression: function (expr, precedence, flags) {
2289 return this.ComprehensionExpression(expr, precedence, flags);
2290 },
2291
2292 ComprehensionExpression: function (expr, precedence, flags) {
2293 // GeneratorExpression should be parenthesized with (...), ComprehensionExpression with [...]
2294 // Due to https://bugzilla.mozilla.org/show_bug.cgi?id=883468 position of expr.body can differ in Spidermonkey and ES6
2295
2296 var result, i, iz, fragment, that = this;
2297 result = (expr.type === Syntax.GeneratorExpression) ? ['('] : ['['];
2298
2299 if (extra.moz.comprehensionExpressionStartsWithAssignment) {
2300 fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
2301 result.push(fragment);
2302 }
2303
2304 if (expr.blocks) {
2305 withIndent(function () {
2306 for (i = 0, iz = expr.blocks.length; i < iz; ++i) {
2307 fragment = that.generateExpression(expr.blocks[i], Precedence.Sequence, E_TTT);
2308 if (i > 0 || extra.moz.comprehensionExpressionStartsWithAssignment) {
2309 result = join(result, fragment);
2310 } else {
2311 result.push(fragment);
2312 }
2313 }
2314 });
2315 }
2316
2317 if (expr.filter) {
2318 result = join(result, 'if' + space);
2319 fragment = this.generateExpression(expr.filter, Precedence.Sequence, E_TTT);
2320 result = join(result, [ '(', fragment, ')' ]);
2321 }
2322
2323 if (!extra.moz.comprehensionExpressionStartsWithAssignment) {
2324 fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
2325
2326 result = join(result, fragment);
2327 }
2328
2329 result.push((expr.type === Syntax.GeneratorExpression) ? ')' : ']');
2330 return result;
2331 },
2332
2333 ComprehensionBlock: function (expr, precedence, flags) {
2334 var fragment;
2335 if (expr.left.type === Syntax.VariableDeclaration) {
2336 fragment = [
2337 expr.left.kind, noEmptySpace(),
2338 this.generateStatement(expr.left.declarations[0], S_FFFF)
2339 ];
2340 } else {
2341 fragment = this.generateExpression(expr.left, Precedence.Call, E_TTT);
2342 }
2343
2344 fragment = join(fragment, expr.of ? 'of' : 'in');
2345 fragment = join(fragment, this.generateExpression(expr.right, Precedence.Sequence, E_TTT));
2346
2347 return [ 'for' + space + '(', fragment, ')' ];
2348 },
2349
2350 SpreadElement: function (expr, precedence, flags) {
2351 return [
2352 '...',
2353 this.generateExpression(expr.argument, Precedence.Assignment, E_TTT)
2354 ];
2355 },
2356
2357 TaggedTemplateExpression: function (expr, precedence, flags) {
2358 var itemFlags = E_TTF;
2359 if (!(flags & F_ALLOW_CALL)) {
2360 itemFlags = E_TFF;
2361 }
2362 var result = [
2363 this.generateExpression(expr.tag, Precedence.Call, itemFlags),
2364 this.generateExpression(expr.quasi, Precedence.Primary, E_FFT)
2365 ];
2366 return parenthesize(result, Precedence.TaggedTemplate, precedence);
2367 },
2368
2369 TemplateElement: function (expr, precedence, flags) {
2370 // Don't use "cooked". Since tagged template can use raw template
2371 // representation. So if we do so, it breaks the script semantics.
2372 return expr.value.raw;
2373 },
2374
2375 TemplateLiteral: function (expr, precedence, flags) {
2376 var result, i, iz;
2377 result = [ '`' ];
2378 for (i = 0, iz = expr.quasis.length; i < iz; ++i) {
2379 result.push(this.generateExpression(expr.quasis[i], Precedence.Primary, E_TTT));
2380 if (i + 1 < iz) {
2381 result.push('${' + space);
2382 result.push(this.generateExpression(expr.expressions[i], Precedence.Sequence, E_TTT));
2383 result.push(space + '}');
2384 }
2385 }
2386 result.push('`');
2387 return result;
2388 },
2389
2390 ModuleSpecifier: function (expr, precedence, flags) {
2391 return this.Literal(expr, precedence, flags);
2392 }
2393
2394 };
2395
2396 merge(CodeGenerator.prototype, CodeGenerator.Expression);
2397
2398 CodeGenerator.prototype.generateExpression = function (expr, precedence, flags) {
2399 var result, type;
2400
2401 type = expr.type || Syntax.Property;
2402
2403 if (extra.verbatim && expr.hasOwnProperty(extra.verbatim)) {
2404 return generateVerbatim(expr, precedence);
2405 }
2406
2407 result = this[type](expr, precedence, flags);
2408
2409
2410 if (extra.comment) {
2411 result = addComments(expr,result);
2412 }
2413 return toSourceNodeWhenNeeded(result, expr);
2414 };
2415
2416 CodeGenerator.prototype.generateStatement = function (stmt, flags) {
2417 var result,
2418 fragment;
2419
2420 result = this[stmt.type](stmt, flags);
2421
2422 // Attach comments
2423
2424 if (extra.comment) {
2425 result = addComments(stmt, result);
2426 }
2427
2428 fragment = toSourceNodeWhenNeeded(result).toString();
2429 if (stmt.type === Syntax.Program && !safeConcatenation && newline === '' && fragment.charAt(fragment.length - 1) === '\n') {
2430 result = sourceMap ? toSourceNodeWhenNeeded(result).replaceRight(/\s+$/, '') : fragment.replace(/\s+$/, '');
2431 }
2432
2433 return toSourceNodeWhenNeeded(result, stmt);
2434 };
2435
2436 function generateInternal(node) {
2437 var codegen;
2438
2439 codegen = new CodeGenerator();
2440 if (isStatement(node)) {
2441 return codegen.generateStatement(node, S_TFFF);
2442 }
2443
2444 if (isExpression(node)) {
2445 return codegen.generateExpression(node, Precedence.Sequence, E_TTT);
2446 }
2447
2448 throw new Error('Unknown node type: ' + node.type);
2449 }
2450
2451 function generate(node, options) {
2452 var defaultOptions = getDefaultOptions(), result, pair;
2453
2454 if (options != null) {
2455 // Obsolete options
2456 //
2457 // `options.indent`
2458 // `options.base`
2459 //
2460 // Instead of them, we can use `option.format.indent`.
2461 if (typeof options.indent === 'string') {
2462 defaultOptions.format.indent.style = options.indent;
2463 }
2464 if (typeof options.base === 'number') {
2465 defaultOptions.format.indent.base = options.base;
2466 }
2467 options = updateDeeply(defaultOptions, options);
2468 indent = options.format.indent.style;
2469 if (typeof options.base === 'string') {
2470 base = options.base;
2471 } else {
2472 base = stringRepeat(indent, options.format.indent.base);
2473 }
2474 } else {
2475 options = defaultOptions;
2476 indent = options.format.indent.style;
2477 base = stringRepeat(indent, options.format.indent.base);
2478 }
2479 json = options.format.json;
2480 renumber = options.format.renumber;
2481 hexadecimal = json ? false : options.format.hexadecimal;
2482 quotes = json ? 'double' : options.format.quotes;
2483 escapeless = options.format.escapeless;
2484 newline = options.format.newline;
2485 space = options.format.space;
2486 if (options.format.compact) {
2487 newline = space = indent = base = '';
2488 }
2489 parentheses = options.format.parentheses;
2490 semicolons = options.format.semicolons;
2491 safeConcatenation = options.format.safeConcatenation;
2492 directive = options.directive;
2493 parse = json ? null : options.parse;
2494 sourceMap = options.sourceMap;
2495 sourceCode = options.sourceCode;
2496 preserveBlankLines = options.format.preserveBlankLines && sourceCode !== null;
2497 extra = options;
2498
2499 if (sourceMap) {
2500 if (!exports.browser) {
2501 // We assume environment is node.js
2502 // And prevent from including source-map by browserify
2503 SourceNode = require('source-map').SourceNode;
2504 } else {
2505 SourceNode = global.sourceMap.SourceNode;
2506 }
2507 }
2508
2509 result = generateInternal(node);
2510
2511 if (!sourceMap) {
2512 pair = {code: result.toString(), map: null};
2513 return options.sourceMapWithCode ? pair : pair.code;
2514 }
2515
2516
2517 pair = result.toStringWithSourceMap({
2518 file: options.file,
2519 sourceRoot: options.sourceMapRoot
2520 });
2521
2522 if (options.sourceContent) {
2523 pair.map.setSourceContent(options.sourceMap,
2524 options.sourceContent);
2525 }
2526
2527 if (options.sourceMapWithCode) {
2528 return pair;
2529 }
2530
2531 return pair.map.toString();
2532 }
2533
2534 FORMAT_MINIFY = {
2535 indent: {
2536 style: '',
2537 base: 0
2538 },
2539 renumber: true,
2540 hexadecimal: true,
2541 quotes: 'auto',
2542 escapeless: true,
2543 compact: true,
2544 parentheses: false,
2545 semicolons: false
2546 };
2547
2548 FORMAT_DEFAULTS = getDefaultOptions().format;
2549
2550 exports.version = require('./package.json').version;
2551 exports.generate = generate;
2552 exports.attachComments = estraverse.attachComments;
2553 exports.Precedence = updateDeeply({}, Precedence);
2554 exports.browser = false;
2555 exports.FORMAT_MINIFY = FORMAT_MINIFY;
2556 exports.FORMAT_DEFAULTS = FORMAT_DEFAULTS;
2557}());
2558/* vim: set sw=4 ts=4 et tw=80 : */