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>
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are met:
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.
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.
36 /*global exports:true, require:true, global:true*/
68 estraverse = require('estraverse');
69 esutils = require('esutils');
71 Syntax = estraverse.Syntax;
73 // Generation is done by generateExpression.
74 function isExpression(node) {
75 return CodeGenerator.Expression.hasOwnProperty(node.type);
78 // Generation is done by generateStatement.
79 function isStatement(node) {
80 return CodeGenerator.Statement.hasOwnProperty(node.type);
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
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;
145 //Expression flag sets
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,
154 E_FFT = F_ALLOW_UNPARATH_NEW,
155 E_TFT = F_ALLOW_IN | F_ALLOW_UNPARATH_NEW;
157 //Statement flag sets
163 var S_TFFF = F_ALLOW_IN,
164 S_TFFT = F_ALLOW_IN | F_SEMICOLON_OPT,
166 S_TFTF = F_ALLOW_IN | F_DIRECTIVE_CTX,
167 S_TTFF = F_ALLOW_IN | F_FUNC_BODY;
169 function getDefaultOptions() {
180 adjustMultilineComment: false
192 safeConcatenation: false,
193 preserveBlankLines: false
196 comprehensionExpressionStartsWithAssignment: false,
197 starlessGenerator: false
201 sourceMapWithCode: false,
209 function stringRepeat(str, num) {
212 for (num |= 0; num > 0; num >>>= 1, str += str) {
221 isArray = Array.isArray;
223 isArray = function isArray(array) {
224 return Object.prototype.toString.call(array) === '[object Array]';
228 function hasLineTerminator(str) {
229 return (/[\r\n]/g).test(str);
232 function endsWithLineTerminator(str) {
233 var len = str.length;
234 return len && esutils.code.isLineTerminator(str.charCodeAt(len - 1));
237 function merge(target, override) {
239 for (key in override) {
240 if (override.hasOwnProperty(key)) {
241 target[key] = override[key];
247 function updateDeeply(target, override) {
250 function isHashObject(target) {
251 return typeof target === 'object' && target instanceof Object && !(target instanceof RegExp);
254 for (key in override) {
255 if (override.hasOwnProperty(key)) {
257 if (isHashObject(val)) {
258 if (isHashObject(target[key])) {
259 updateDeeply(target[key], val);
261 target[key] = updateDeeply({}, val);
271 function generateNumber(value) {
272 var result, point, temp, exponent, pos;
274 if (value !== value) {
275 throw new Error('Numeric literal whose value is NaN');
277 if (value < 0 || (value === 0 && 1 / value < 0)) {
278 throw new Error('Numeric literal whose value is negative');
281 if (value === 1 / 0) {
282 return json ? 'null' : renumber ? '1e400' : '1e+400';
286 if (!renumber || result.length < 3) {
290 point = result.indexOf('.');
291 if (!json && result.charCodeAt(0) === 0x30 /* 0 */ && point === 1) {
293 result = result.slice(1);
296 result = result.replace('e+', 'e');
298 if ((pos = temp.indexOf('e')) > 0) {
299 exponent = +temp.slice(pos + 1);
300 temp = temp.slice(0, pos);
303 exponent -= temp.length - point - 1;
304 temp = +(temp.slice(0, point) + temp.slice(point + 1)) + '';
307 while (temp.charCodeAt(temp.length + pos - 1) === 0x30 /* 0 */) {
312 temp = temp.slice(0, pos);
314 if (exponent !== 0) {
315 temp += 'e' + exponent;
317 if ((temp.length < result.length ||
318 (hexadecimal && value > 1e12 && Math.floor(value) === value && (temp = '0x' + value.toString(16)).length < result.length)) &&
326 // Generate valid RegExp expression.
327 // This function is based on https://github.com/Constellation/iv Engine
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');
336 return String.fromCharCode(ch);
339 function generateRegExp(reg) {
340 var match, result, flags, i, iz, ch, characterInBrack, previousIsBackslash;
342 result = reg.toString();
345 // extract flag from toString result
346 match = result.match(/\/([^/]*)$/);
354 characterInBrack = false;
355 previousIsBackslash = false;
356 for (i = 0, iz = reg.source.length; i < iz; ++i) {
357 ch = reg.source.charCodeAt(i);
359 if (!previousIsBackslash) {
360 if (characterInBrack) {
361 if (ch === 93) { // ]
362 characterInBrack = false;
365 if (ch === 47) { // /
367 } else if (ch === 91) { // [
368 characterInBrack = true;
371 result += escapeRegExpCharacter(ch, previousIsBackslash);
372 previousIsBackslash = ch === 92; // \
374 // if new RegExp("\\\n') is provided, create /\n/
375 result += escapeRegExpCharacter(ch, previousIsBackslash);
376 // prevent like /\\[/]/
377 previousIsBackslash = false;
381 return '/' + result + '/' + flags;
387 function escapeAllowedCharacter(code, next) {
390 if (code === 0x08 /* \b */) {
394 if (code === 0x0C /* \f */) {
398 if (code === 0x09 /* \t */) {
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)) {
407 } else if (code === 0x000B /* \v */) { // '\v'
410 return '\\x' + '00'.slice(hex.length) + hex;
414 function escapeDisallowedCharacter(code) {
415 if (code === 0x5C /* \ */) {
419 if (code === 0x0A /* \n */) {
423 if (code === 0x0D /* \r */) {
427 if (code === 0x2028) {
431 if (code === 0x2029) {
435 throw new Error('Incorrectly classified character');
438 function escapeDirective(str) {
439 var i, iz, code, quote;
441 quote = quotes === 'double' ? '"' : '\'';
442 for (i = 0, iz = str.length; i < iz; ++i) {
443 code = str.charCodeAt(i);
444 if (code === 0x27 /* ' */) {
447 } else if (code === 0x22 /* " */) {
450 } else if (code === 0x5C /* \ */) {
455 return quote + str + quote;
458 function escapeString(str) {
459 var result = '', i, len, code, singleQuotes = 0, doubleQuotes = 0, single, quote;
461 for (i = 0, len = str.length; i < len; ++i) {
462 code = str.charCodeAt(i);
463 if (code === 0x27 /* ' */) {
465 } else if (code === 0x22 /* " */) {
467 } else if (code === 0x2F /* / */ && json) {
469 } else if (esutils.code.isLineTerminator(code) || code === 0x5C /* \ */) {
470 result += escapeDisallowedCharacter(code);
472 } else if (!esutils.code.isIdentifierPartES5(code) && (json && code < 0x20 /* SP */ || !json && !escapeless && (code < 0x20 /* SP */ || code > 0x7E /* ~ */))) {
473 result += escapeAllowedCharacter(code, str.charCodeAt(i + 1));
476 result += String.fromCharCode(code);
479 single = !(quotes === 'double' || (quotes === 'auto' && doubleQuotes < singleQuotes));
480 quote = single ? '\'' : '"';
482 if (!(single ? singleQuotes : doubleQuotes)) {
483 return quote + result + quote;
489 for (i = 0, len = str.length; i < len; ++i) {
490 code = str.charCodeAt(i);
491 if ((code === 0x27 /* ' */ && single) || (code === 0x22 /* " */ && !single)) {
494 result += String.fromCharCode(code);
497 return result + quote;
501 * flatten an array to a string, where the array can contain
502 * either strings or nested arrays
504 function flattenToString(arr) {
505 var i, iz, elem, result = '';
506 for (i = 0, iz = arr.length; i < iz; ++i) {
508 result += isArray(elem) ? flattenToString(elem) : elem;
514 * convert generated to a SourceNode when source maps are enabled.
516 function toSourceNodeWhenNeeded(generated, node) {
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);
528 if (generated instanceof SourceNode) {
534 if (node.loc == null) {
535 return new SourceNode(null, null, sourceMap, generated, node.name || null);
537 return new SourceNode(node.loc.start.line, node.loc.start.column, (sourceMap === true ? node.loc.source || null : sourceMap), generated, node.name || null);
540 function noEmptySpace() {
541 return (space) ? space : ' ';
544 function join(left, right) {
550 leftSource = toSourceNodeWhenNeeded(left).toString();
551 if (leftSource.length === 0) {
555 rightSource = toSourceNodeWhenNeeded(right).toString();
556 if (rightSource.length === 0) {
560 leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
561 rightCharCode = rightSource.charCodeAt(0);
563 if ((leftCharCode === 0x2B /* + */ || leftCharCode === 0x2D /* - */) && leftCharCode === rightCharCode ||
564 esutils.code.isIdentifierPartES5(leftCharCode) && esutils.code.isIdentifierPartES5(rightCharCode) ||
565 leftCharCode === 0x2F /* / */ && rightCharCode === 0x69 /* i */) { // infix word operators all start with `i`
566 return [left, noEmptySpace(), right];
567 } else if (esutils.code.isWhiteSpace(leftCharCode) || esutils.code.isLineTerminator(leftCharCode) ||
568 esutils.code.isWhiteSpace(rightCharCode) || esutils.code.isLineTerminator(rightCharCode)) {
569 return [left, right];
571 return [left, space, right];
574 function addIndent(stmt) {
578 function withIndent(fn) {
586 function calculateSpaces(str) {
588 for (i = str.length - 1; i >= 0; --i) {
589 if (esutils.code.isLineTerminator(str.charCodeAt(i))) {
593 return (str.length - 1) - i;
596 function adjustMultilineComment(value, specialBase) {
597 var array, i, len, line, j, spaces, previousBase, sn;
599 array = value.split(/\r\n|[\r\n]/);
600 spaces = Number.MAX_VALUE;
602 // first line doesn't have indentation
603 for (i = 1, len = array.length; i < len; ++i) {
606 while (j < line.length && esutils.code.isWhiteSpace(line.charCodeAt(j))) {
614 if (typeof specialBase !== 'undefined') {
622 if (array[1][spaces] === '*') {
631 // If spaces are odd number, above pattern is considered.
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;
645 return array.join('\n');
648 function generateComment(comment, specialBase) {
649 if (comment.type === 'Line') {
650 if (endsWithLineTerminator(comment.value)) {
651 return '//' + comment.value;
653 // Always use LineTerminator
654 var result = '//' + comment.value;
655 if (!preserveBlankLines) {
661 if (extra.format.indent.adjustMultilineComment && /[\n\r]/.test(comment.value)) {
662 return adjustMultilineComment('/*' + comment.value + '*/', specialBase);
664 return '/*' + comment.value + '*/';
667 function addComments(stmt, result) {
668 var i, len, comment, save, tailingToStatement, specialBase, fragment,
669 extRange, range, prevRange, prefix, infix, suffix, count;
671 if (stmt.leadingComments && stmt.leadingComments.length > 0) {
674 if (preserveBlankLines) {
675 comment = stmt.leadingComments[0];
678 extRange = comment.extendedRange;
679 range = comment.range;
681 prefix = sourceCode.substring(extRange[0], range[0]);
682 count = (prefix.match(/\n/g) || []).length;
684 result.push(stringRepeat('\n', count));
685 result.push(addIndent(generateComment(comment)));
688 result.push(generateComment(comment));
693 for (i = 1, len = stmt.leadingComments.length; i < len; i++) {
694 comment = stmt.leadingComments[i];
695 range = comment.range;
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)));
705 suffix = sourceCode.substring(range[1], extRange[1]);
706 count = (suffix.match(/\n/g) || []).length;
707 result.push(stringRepeat('\n', count));
709 comment = stmt.leadingComments[0];
711 if (safeConcatenation && stmt.type === Syntax.Program && stmt.body.length === 0) {
714 result.push(generateComment(comment));
715 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
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())) {
725 result.push(addIndent(fragment));
729 result.push(addIndent(save));
732 if (stmt.trailingComments) {
734 if (preserveBlankLines) {
735 comment = stmt.trailingComments[0];
736 extRange = comment.extendedRange;
737 range = comment.range;
739 prefix = sourceCode.substring(extRange[0], range[0]);
740 count = (prefix.match(/\n/g) || []).length;
743 result.push(stringRepeat('\n', count));
744 result.push(addIndent(generateComment(comment)));
747 result.push(generateComment(comment));
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
758 // * This is comment of t
762 result = [result, indent];
764 result = [result, specialBase];
766 result.push(generateComment(comment, specialBase));
768 result = [result, addIndent(generateComment(comment))];
770 if (i !== len - 1 && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
771 result = [result, '\n'];
780 function generateBlankLines(start, end, result) {
781 var j, newlineCount = 0;
783 for (j = start; j < end; j++) {
784 if (sourceCode[j] === '\n') {
789 for (j = 1; j < newlineCount; j++) {
790 result.push(newline);
794 function parenthesize(text, current, should) {
795 if (current < should) {
796 return ['(', text, ')'];
801 function generateVerbatimString(string) {
803 result = string.split(/\r\n|\n/);
804 for (i = 1, iz = result.length; i < iz; i++) {
805 result[i] = newline + base + result[i];
810 function generateVerbatim(expr, precedence) {
811 var verbatim, result, prec;
812 verbatim = expr[extra.verbatim];
814 if (typeof verbatim === 'string') {
815 result = parenthesize(generateVerbatimString(verbatim), Precedence.Sequence, precedence);
817 // verbatim is object
818 result = generateVerbatimString(verbatim.content);
819 prec = (verbatim.precedence != null) ? verbatim.precedence : Precedence.Sequence;
820 result = parenthesize(result, prec, precedence);
823 return toSourceNodeWhenNeeded(result, expr);
826 function CodeGenerator() {
831 CodeGenerator.prototype.maybeBlock = function(stmt, flags) {
832 var result, noLeadingComment, that = this;
834 noLeadingComment = !extra.comment || !stmt.leadingComments;
836 if (stmt.type === Syntax.BlockStatement && noLeadingComment) {
837 return [space, this.generateStatement(stmt, flags)];
840 if (stmt.type === Syntax.EmptyStatement && noLeadingComment) {
844 withIndent(function () {
847 addIndent(that.generateStatement(stmt, flags))
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];
860 return [result, base];
862 return [result, newline, base];
865 function generateIdentifier(node) {
866 return toSourceNodeWhenNeeded(node.name, node);
869 function generateAsyncPrefix(node, spaceRequired) {
870 return node.async ? 'async' + (spaceRequired ? noEmptySpace() : space) : '';
873 function generateStarSuffix(node) {
874 var isGenerator = node.generator && !extra.moz.starlessGenerator;
875 return isGenerator ? '*' + space : '';
878 function generateMethodPrefix(prop) {
879 var func = prop.value;
881 return generateAsyncPrefix(func, !prop.computed);
883 // avoid space before method name
884 return generateStarSuffix(func) ? '*' : '';
888 CodeGenerator.prototype.generatePattern = function (node, precedence, flags) {
889 if (node.type === Syntax.Identifier) {
890 return generateIdentifier(node);
892 return this.generateExpression(node, precedence, flags);
895 CodeGenerator.prototype.generateFunctionParams = function (node) {
896 var i, iz, result, hasDefault;
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) {
904 result = [generateAsyncPrefix(node, true), generateIdentifier(node.params[0])];
906 result = node.type === Syntax.ArrowFunctionExpression ? [generateAsyncPrefix(node, false)] : [];
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));
916 result.push(this.generatePattern(node.params[i], Precedence.Assignment, E_TTT));
919 result.push(',' + space);
924 if (node.params.length) {
925 result.push(',' + space);
928 result.push(generateIdentifier(node.rest));
937 CodeGenerator.prototype.generateFunctionBody = function (node) {
940 result = this.generateFunctionParams(node);
942 if (node.type === Syntax.ArrowFunctionExpression) {
947 if (node.expression) {
949 expr = this.generateExpression(node.body, Precedence.Assignment, E_TTT);
950 if (expr.toString().charAt(0) === '{') {
951 expr = ['(', expr, ')'];
955 result.push(this.maybeBlock(node.body, S_TTFF));
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));
970 result.push(that.generateExpression(stmt.left, Precedence.Call, E_TTT));
973 result = join(result, operator);
976 that.generateExpression(stmt.right, Precedence.Sequence, E_TTT)
979 result.push(this.maybeBlock(stmt.body, flags));
983 CodeGenerator.prototype.generatePropertyKey = function (expr, computed) {
990 result.push(this.generateExpression(expr, Precedence.Sequence, E_TTT));
998 CodeGenerator.prototype.generateAssignment = function (left, right, operator, precedence, flags) {
999 if (Precedence.Assignment < precedence) {
1000 flags |= F_ALLOW_IN;
1003 return parenthesize(
1005 this.generateExpression(left, Precedence.Call, flags),
1006 space + operator + space,
1007 this.generateExpression(right, Precedence.Assignment, flags)
1009 Precedence.Assignment,
1014 CodeGenerator.prototype.semicolon = function (flags) {
1015 if (!semicolons && flags & F_SEMICOLON_OPT) {
1023 CodeGenerator.Statement = {
1025 BlockStatement: function (stmt, flags) {
1026 var range, content, result = ['{', newline], that = this;
1028 withIndent(function () {
1029 // handle functions without any code
1030 if (stmt.body.length === 0 && preserveBlankLines) {
1032 if (range[1] - range[0] > 2) {
1033 content = sourceCode.substring(range[0] + 1, range[1] - 1);
1034 if (content[0] === '\n') {
1037 result.push(content);
1041 var i, iz, fragment, bodyFlags;
1043 if (flags & F_FUNC_BODY) {
1044 bodyFlags |= F_DIRECTIVE_CTX;
1047 for (i = 0, iz = stmt.body.length; i < iz; ++i) {
1048 if (preserveBlankLines) {
1049 // handle spaces before the first line
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') {
1058 if (!stmt.body[0].leadingComments) {
1059 generateBlankLines(stmt.range[0], stmt.body[0].range[0], result);
1063 // handle spaces between lines
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);
1072 bodyFlags |= F_SEMICOLON_OPT;
1075 if (stmt.body[i].leadingComments && preserveBlankLines) {
1076 fragment = that.generateStatement(stmt.body[i], bodyFlags);
1078 fragment = addIndent(that.generateStatement(stmt.body[i], bodyFlags));
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);
1090 result.push(newline);
1094 if (preserveBlankLines) {
1095 // handle spaces after the last line
1097 if (!stmt.body[i].trailingComments) {
1098 generateBlankLines(stmt.body[i].range[1], stmt.range[1], result);
1105 result.push(addIndent('}'));
1109 BreakStatement: function (stmt, flags) {
1111 return 'break ' + stmt.label.name + this.semicolon(flags);
1113 return 'break' + this.semicolon(flags);
1116 ContinueStatement: function (stmt, flags) {
1118 return 'continue ' + stmt.label.name + this.semicolon(flags);
1120 return 'continue' + this.semicolon(flags);
1123 ClassBody: function (stmt, flags) {
1124 var result = [ '{', newline], that = this;
1126 withIndent(function (indent) {
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));
1133 result.push(newline);
1138 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1139 result.push(newline);
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);
1154 result.push(this.generateStatement(stmt.body, S_TFFT));
1158 DirectiveStatement: function (stmt, flags) {
1159 if (extra.raw && stmt.raw) {
1160 return stmt.raw + this.semicolon(flags);
1162 return escapeDirective(stmt.directive) + this.semicolon(flags);
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)
1176 CatchClause: function (stmt, flags) {
1177 var result, that = this;
1178 withIndent(function () {
1182 'catch' + space + '(',
1183 that.generateExpression(stmt.param, Precedence.Sequence, E_TTT),
1188 guard = that.generateExpression(stmt.guard, Precedence.Sequence, E_TTT);
1189 result.splice(2, 0, ' if ', guard);
1192 result.push(this.maybeBlock(stmt.body, S_TFFF));
1196 DebuggerStatement: function (stmt, flags) {
1197 return 'debugger' + this.semicolon(flags);
1200 EmptyStatement: function (stmt, flags) {
1204 ExportDeclaration: function (stmt, flags) {
1205 var result = [ 'export' ], bodyFlags, that = this;
1207 bodyFlags = (flags & F_SEMICOLON_OPT) ? S_TFFT : S_TFFF;
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));
1216 result = join(result, this.generateExpression(stmt.declaration, Precedence.Assignment, E_TTT) + this.semicolon(flags));
1221 // export VariableStatement
1222 // export Declaration[Default]
1223 if (stmt.declaration) {
1224 return join(result, this.generateStatement(stmt.declaration, bodyFlags));
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));
1236 result = join(result, '{');
1237 withIndent(function (indent) {
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));
1244 result.push(',' + newline);
1248 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1249 result.push(newline);
1251 result.push(base + '}');
1255 result = join(result, [
1258 this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1259 this.semicolon(flags)
1262 result.push(this.semicolon(flags));
1268 ExportDefaultDeclaration: function (stmt, flags) {
1269 stmt.default = true;
1270 return this.ExportDeclaration(stmt, flags);
1273 ExportNamedDeclaration: function (stmt, flags) {
1274 return this.ExportDeclaration(stmt, flags);
1277 ExpressionStatement: function (stmt, flags) {
1278 var result, fragment;
1280 function isClassPrefixed(fragment) {
1282 if (fragment.slice(0, 5) !== 'class') {
1285 code = fragment.charCodeAt(5);
1286 return code === 0x7B /* '{' */ || esutils.code.isWhiteSpace(code) || esutils.code.isLineTerminator(code);
1289 function isFunctionPrefixed(fragment) {
1291 if (fragment.slice(0, 8) !== 'function') {
1294 code = fragment.charCodeAt(8);
1295 return code === 0x28 /* '(' */ || esutils.code.isWhiteSpace(code) || code === 0x2A /* '*' */ || esutils.code.isLineTerminator(code);
1298 function isAsyncPrefixed(fragment) {
1300 if (fragment.slice(0, 5) !== 'async') {
1303 if (!esutils.code.isWhiteSpace(fragment.charCodeAt(5))) {
1306 for (i = 6, iz = fragment.length; i < iz; ++i) {
1307 if (!esutils.code.isWhiteSpace(fragment.charCodeAt(i))) {
1314 if (fragment.slice(i, i + 8) !== 'function') {
1317 code = fragment.charCodeAt(i + 8);
1318 return code === 0x28 /* '(' */ || esutils.code.isWhiteSpace(code) || code === 0x2A /* '*' */ || esutils.code.isLineTerminator(code);
1321 result = [this.generateExpression(stmt.expression, Precedence.Sequence, E_TTT)];
1322 // 12.4 '{', 'function', 'class' is not allowed in this position.
1323 // wrap expression with parentheses
1324 fragment = toSourceNodeWhenNeeded(result).toString();
1325 if (fragment.charCodeAt(0) === 0x7B /* '{' */ || // ObjectExpression
1326 isClassPrefixed(fragment) ||
1327 isFunctionPrefixed(fragment) ||
1328 isAsyncPrefixed(fragment) ||
1329 (directive && (flags & F_DIRECTIVE_CTX) && stmt.expression.type === Syntax.Literal && typeof stmt.expression.value === 'string')) {
1330 result = ['(', result, ')' + this.semicolon(flags)];
1332 result.push(this.semicolon(flags));
1337 ImportDeclaration: function (stmt, flags) {
1338 // ES6: 15.2.1 valid import declarations:
1339 // - import ImportClause FromClause ;
1340 // - import ModuleSpecifier ;
1341 var result, cursor, that = this;
1343 // If no ImportClause is present,
1344 // this should be `import ModuleSpecifier` so skip `from`
1345 // ModuleSpecifier is StringLiteral.
1346 if (stmt.specifiers.length === 0) {
1347 // import ModuleSpecifier ;
1352 this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1353 this.semicolon(flags)
1357 // import ImportClause FromClause ;
1364 if (stmt.specifiers[cursor].type === Syntax.ImportDefaultSpecifier) {
1365 result = join(result, [
1366 this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
1371 if (stmt.specifiers[cursor]) {
1376 if (stmt.specifiers[cursor].type === Syntax.ImportNamespaceSpecifier) {
1378 result = join(result, [
1380 this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
1384 result.push(space + '{');
1386 if ((stmt.specifiers.length - cursor) === 1) {
1387 // import { ... } from "...";
1389 result.push(this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT));
1390 result.push(space + '}' + space);
1396 withIndent(function (indent) {
1398 result.push(newline);
1399 for (i = cursor, iz = stmt.specifiers.length; i < iz; ++i) {
1400 result.push(indent);
1401 result.push(that.generateExpression(stmt.specifiers[i], Precedence.Sequence, E_TTT));
1403 result.push(',' + newline);
1407 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1408 result.push(newline);
1410 result.push(base + '}' + space);
1415 result = join(result, [
1418 this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
1419 this.semicolon(flags)
1424 VariableDeclarator: function (stmt, flags) {
1425 var itemFlags = (flags & F_ALLOW_IN) ? E_TTT : E_FTT;
1428 this.generateExpression(stmt.id, Precedence.Assignment, itemFlags),
1432 this.generateExpression(stmt.init, Precedence.Assignment, itemFlags)
1435 return this.generatePattern(stmt.id, Precedence.Assignment, itemFlags);
1438 VariableDeclaration: function (stmt, flags) {
1439 // VariableDeclarator is typed as Statement,
1440 // but joined with comma (not LineTerminator).
1441 // So if comment is attached to target node, we should specialize.
1442 var result, i, iz, node, bodyFlags, that = this;
1444 result = [ stmt.kind ];
1446 bodyFlags = (flags & F_ALLOW_IN) ? S_TFFF : S_FFFF;
1449 node = stmt.declarations[0];
1450 if (extra.comment && node.leadingComments) {
1452 result.push(addIndent(that.generateStatement(node, bodyFlags)));
1454 result.push(noEmptySpace());
1455 result.push(that.generateStatement(node, bodyFlags));
1458 for (i = 1, iz = stmt.declarations.length; i < iz; ++i) {
1459 node = stmt.declarations[i];
1460 if (extra.comment && node.leadingComments) {
1461 result.push(',' + newline);
1462 result.push(addIndent(that.generateStatement(node, bodyFlags)));
1464 result.push(',' + space);
1465 result.push(that.generateStatement(node, bodyFlags));
1470 if (stmt.declarations.length > 1) {
1476 result.push(this.semicolon(flags));
1481 ThrowStatement: function (stmt, flags) {
1484 this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
1485 ), this.semicolon(flags)];
1488 TryStatement: function (stmt, flags) {
1489 var result, i, iz, guardedHandlers;
1491 result = ['try', this.maybeBlock(stmt.block, S_TFFF)];
1492 result = this.maybeBlockSuffix(stmt.block, result);
1494 if (stmt.handlers) {
1496 for (i = 0, iz = stmt.handlers.length; i < iz; ++i) {
1497 result = join(result, this.generateStatement(stmt.handlers[i], S_TFFF));
1498 if (stmt.finalizer || i + 1 !== iz) {
1499 result = this.maybeBlockSuffix(stmt.handlers[i].body, result);
1503 guardedHandlers = stmt.guardedHandlers || [];
1505 for (i = 0, iz = guardedHandlers.length; i < iz; ++i) {
1506 result = join(result, this.generateStatement(guardedHandlers[i], S_TFFF));
1507 if (stmt.finalizer || i + 1 !== iz) {
1508 result = this.maybeBlockSuffix(guardedHandlers[i].body, result);
1514 if (isArray(stmt.handler)) {
1515 for (i = 0, iz = stmt.handler.length; i < iz; ++i) {
1516 result = join(result, this.generateStatement(stmt.handler[i], S_TFFF));
1517 if (stmt.finalizer || i + 1 !== iz) {
1518 result = this.maybeBlockSuffix(stmt.handler[i].body, result);
1522 result = join(result, this.generateStatement(stmt.handler, S_TFFF));
1523 if (stmt.finalizer) {
1524 result = this.maybeBlockSuffix(stmt.handler.body, result);
1529 if (stmt.finalizer) {
1530 result = join(result, ['finally', this.maybeBlock(stmt.finalizer, S_TFFF)]);
1535 SwitchStatement: function (stmt, flags) {
1536 var result, fragment, i, iz, bodyFlags, that = this;
1537 withIndent(function () {
1539 'switch' + space + '(',
1540 that.generateExpression(stmt.discriminant, Precedence.Sequence, E_TTT),
1541 ')' + space + '{' + newline
1546 for (i = 0, iz = stmt.cases.length; i < iz; ++i) {
1548 bodyFlags |= F_SEMICOLON_OPT;
1550 fragment = addIndent(this.generateStatement(stmt.cases[i], bodyFlags));
1551 result.push(fragment);
1552 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1553 result.push(newline);
1557 result.push(addIndent('}'));
1561 SwitchCase: function (stmt, flags) {
1562 var result, fragment, i, iz, bodyFlags, that = this;
1563 withIndent(function () {
1566 join('case', that.generateExpression(stmt.test, Precedence.Sequence, E_TTT)),
1570 result = ['default:'];
1574 iz = stmt.consequent.length;
1575 if (iz && stmt.consequent[0].type === Syntax.BlockStatement) {
1576 fragment = that.maybeBlock(stmt.consequent[0], S_TFFF);
1577 result.push(fragment);
1581 if (i !== iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
1582 result.push(newline);
1586 for (; i < iz; ++i) {
1587 if (i === iz - 1 && flags & F_SEMICOLON_OPT) {
1588 bodyFlags |= F_SEMICOLON_OPT;
1590 fragment = addIndent(that.generateStatement(stmt.consequent[i], bodyFlags));
1591 result.push(fragment);
1592 if (i + 1 !== iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1593 result.push(newline);
1600 IfStatement: function (stmt, flags) {
1601 var result, bodyFlags, semicolonOptional, that = this;
1602 withIndent(function () {
1605 that.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1609 semicolonOptional = flags & F_SEMICOLON_OPT;
1611 if (semicolonOptional) {
1612 bodyFlags |= F_SEMICOLON_OPT;
1614 if (stmt.alternate) {
1615 result.push(this.maybeBlock(stmt.consequent, S_TFFF));
1616 result = this.maybeBlockSuffix(stmt.consequent, result);
1617 if (stmt.alternate.type === Syntax.IfStatement) {
1618 result = join(result, ['else ', this.generateStatement(stmt.alternate, bodyFlags)]);
1620 result = join(result, join('else', this.maybeBlock(stmt.alternate, bodyFlags)));
1623 result.push(this.maybeBlock(stmt.consequent, bodyFlags));
1628 ForStatement: function (stmt, flags) {
1629 var result, that = this;
1630 withIndent(function () {
1631 result = ['for' + space + '('];
1633 if (stmt.init.type === Syntax.VariableDeclaration) {
1634 result.push(that.generateStatement(stmt.init, S_FFFF));
1636 // F_ALLOW_IN becomes false.
1637 result.push(that.generateExpression(stmt.init, Precedence.Sequence, E_FTT));
1646 result.push(that.generateExpression(stmt.test, Precedence.Sequence, E_TTT));
1654 result.push(that.generateExpression(stmt.update, Precedence.Sequence, E_TTT));
1661 result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1665 ForInStatement: function (stmt, flags) {
1666 return this.generateIterationForStatement('in', stmt, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF);
1669 ForOfStatement: function (stmt, flags) {
1670 return this.generateIterationForStatement('of', stmt, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF);
1673 LabeledStatement: function (stmt, flags) {
1674 return [stmt.label.name + ':', this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF)];
1677 Program: function (stmt, flags) {
1678 var result, fragment, i, iz, bodyFlags;
1679 iz = stmt.body.length;
1680 result = [safeConcatenation && iz > 0 ? '\n' : ''];
1682 for (i = 0; i < iz; ++i) {
1683 if (!safeConcatenation && i === iz - 1) {
1684 bodyFlags |= F_SEMICOLON_OPT;
1687 if (preserveBlankLines) {
1688 // handle spaces before the first line
1690 if (!stmt.body[0].leadingComments) {
1691 generateBlankLines(stmt.range[0], stmt.body[i].range[0], result);
1695 // handle spaces between lines
1697 if (!stmt.body[i - 1].trailingComments && !stmt.body[i].leadingComments) {
1698 generateBlankLines(stmt.body[i - 1].range[1], stmt.body[i].range[0], result);
1703 fragment = addIndent(this.generateStatement(stmt.body[i], bodyFlags));
1704 result.push(fragment);
1705 if (i + 1 < iz && !endsWithLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
1706 if (preserveBlankLines) {
1707 if (!stmt.body[i + 1].leadingComments) {
1708 result.push(newline);
1711 result.push(newline);
1715 if (preserveBlankLines) {
1716 // handle spaces after the last line
1718 if (!stmt.body[i].trailingComments) {
1719 generateBlankLines(stmt.body[i].range[1], stmt.range[1], result);
1727 FunctionDeclaration: function (stmt, flags) {
1729 generateAsyncPrefix(stmt, true),
1731 generateStarSuffix(stmt) || noEmptySpace(),
1732 generateIdentifier(stmt.id),
1733 this.generateFunctionBody(stmt)
1737 ReturnStatement: function (stmt, flags) {
1738 if (stmt.argument) {
1741 this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
1742 ), this.semicolon(flags)];
1744 return ['return' + this.semicolon(flags)];
1747 WhileStatement: function (stmt, flags) {
1748 var result, that = this;
1749 withIndent(function () {
1751 'while' + space + '(',
1752 that.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
1756 result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1760 WithStatement: function (stmt, flags) {
1761 var result, that = this;
1762 withIndent(function () {
1764 'with' + space + '(',
1765 that.generateExpression(stmt.object, Precedence.Sequence, E_TTT),
1769 result.push(this.maybeBlock(stmt.body, flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF));
1775 merge(CodeGenerator.prototype, CodeGenerator.Statement);
1779 CodeGenerator.Expression = {
1781 SequenceExpression: function (expr, precedence, flags) {
1783 if (Precedence.Sequence < precedence) {
1784 flags |= F_ALLOW_IN;
1787 for (i = 0, iz = expr.expressions.length; i < iz; ++i) {
1788 result.push(this.generateExpression(expr.expressions[i], Precedence.Assignment, flags));
1790 result.push(',' + space);
1793 return parenthesize(result, Precedence.Sequence, precedence);
1796 AssignmentExpression: function (expr, precedence, flags) {
1797 return this.generateAssignment(expr.left, expr.right, expr.operator, precedence, flags);
1800 ArrowFunctionExpression: function (expr, precedence, flags) {
1801 return parenthesize(this.generateFunctionBody(expr), Precedence.ArrowFunction, precedence);
1804 ConditionalExpression: function (expr, precedence, flags) {
1805 if (Precedence.Conditional < precedence) {
1806 flags |= F_ALLOW_IN;
1808 return parenthesize(
1810 this.generateExpression(expr.test, Precedence.LogicalOR, flags),
1811 space + '?' + space,
1812 this.generateExpression(expr.consequent, Precedence.Assignment, flags),
1813 space + ':' + space,
1814 this.generateExpression(expr.alternate, Precedence.Assignment, flags)
1816 Precedence.Conditional,
1821 LogicalExpression: function (expr, precedence, flags) {
1822 return this.BinaryExpression(expr, precedence, flags);
1825 BinaryExpression: function (expr, precedence, flags) {
1826 var result, currentPrecedence, fragment, leftSource;
1827 currentPrecedence = BinaryPrecedence[expr.operator];
1829 if (currentPrecedence < precedence) {
1830 flags |= F_ALLOW_IN;
1833 fragment = this.generateExpression(expr.left, currentPrecedence, flags);
1835 leftSource = fragment.toString();
1837 if (leftSource.charCodeAt(leftSource.length - 1) === 0x2F /* / */ && esutils.code.isIdentifierPartES5(expr.operator.charCodeAt(0))) {
1838 result = [fragment, noEmptySpace(), expr.operator];
1840 result = join(fragment, expr.operator);
1843 fragment = this.generateExpression(expr.right, currentPrecedence + 1, flags);
1845 if (expr.operator === '/' && fragment.toString().charAt(0) === '/' ||
1846 expr.operator.slice(-1) === '<' && fragment.toString().slice(0, 3) === '!--') {
1847 // If '/' concats with '/' or `<` concats with `!--`, it is interpreted as comment start
1848 result.push(noEmptySpace());
1849 result.push(fragment);
1851 result = join(result, fragment);
1854 if (expr.operator === 'in' && !(flags & F_ALLOW_IN)) {
1855 return ['(', result, ')'];
1857 return parenthesize(result, currentPrecedence, precedence);
1860 CallExpression: function (expr, precedence, flags) {
1862 // F_ALLOW_UNPARATH_NEW becomes false.
1863 result = [this.generateExpression(expr.callee, Precedence.Call, E_TTF)];
1865 for (i = 0, iz = expr['arguments'].length; i < iz; ++i) {
1866 result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
1868 result.push(',' + space);
1873 if (!(flags & F_ALLOW_CALL)) {
1874 return ['(', result, ')'];
1876 return parenthesize(result, Precedence.Call, precedence);
1879 NewExpression: function (expr, precedence, flags) {
1880 var result, length, i, iz, itemFlags;
1881 length = expr['arguments'].length;
1883 // F_ALLOW_CALL becomes false.
1884 // F_ALLOW_UNPARATH_NEW may become false.
1885 itemFlags = (flags & F_ALLOW_UNPARATH_NEW && !parentheses && length === 0) ? E_TFT : E_TFF;
1889 this.generateExpression(expr.callee, Precedence.New, itemFlags)
1892 if (!(flags & F_ALLOW_UNPARATH_NEW) || parentheses || length > 0) {
1894 for (i = 0, iz = length; i < iz; ++i) {
1895 result.push(this.generateExpression(expr['arguments'][i], Precedence.Assignment, E_TTT));
1897 result.push(',' + space);
1903 return parenthesize(result, Precedence.New, precedence);
1906 MemberExpression: function (expr, precedence, flags) {
1907 var result, fragment;
1909 // F_ALLOW_UNPARATH_NEW becomes false.
1910 result = [this.generateExpression(expr.object, Precedence.Call, (flags & F_ALLOW_CALL) ? E_TTF : E_TFF)];
1912 if (expr.computed) {
1914 result.push(this.generateExpression(expr.property, Precedence.Sequence, flags & F_ALLOW_CALL ? E_TTT : E_TFT));
1917 if (expr.object.type === Syntax.Literal && typeof expr.object.value === 'number') {
1918 fragment = toSourceNodeWhenNeeded(result).toString();
1919 // When the following conditions are all true,
1920 // 1. No floating point
1921 // 2. Don't have exponents
1922 // 3. The last character is a decimal digit
1923 // 4. Not hexadecimal OR octal number literal
1924 // we should add a floating point.
1926 fragment.indexOf('.') < 0 &&
1927 !/[eExX]/.test(fragment) &&
1928 esutils.code.isDecimalDigit(fragment.charCodeAt(fragment.length - 1)) &&
1929 !(fragment.length >= 2 && fragment.charCodeAt(0) === 48) // '0'
1935 result.push(generateIdentifier(expr.property));
1938 return parenthesize(result, Precedence.Member, precedence);
1941 UnaryExpression: function (expr, precedence, flags) {
1942 var result, fragment, rightCharCode, leftSource, leftCharCode;
1943 fragment = this.generateExpression(expr.argument, Precedence.Unary, E_TTT);
1946 result = join(expr.operator, fragment);
1948 result = [expr.operator];
1949 if (expr.operator.length > 2) {
1950 // delete, void, typeof
1951 // get `typeof []`, not `typeof[]`
1952 result = join(result, fragment);
1954 // Prevent inserting spaces between operator and argument if it is unnecessary
1956 leftSource = toSourceNodeWhenNeeded(result).toString();
1957 leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
1958 rightCharCode = fragment.toString().charCodeAt(0);
1960 if (((leftCharCode === 0x2B /* + */ || leftCharCode === 0x2D /* - */) && leftCharCode === rightCharCode) ||
1961 (esutils.code.isIdentifierPartES5(leftCharCode) && esutils.code.isIdentifierPartES5(rightCharCode))) {
1962 result.push(noEmptySpace());
1963 result.push(fragment);
1965 result.push(fragment);
1969 return parenthesize(result, Precedence.Unary, precedence);
1972 YieldExpression: function (expr, precedence, flags) {
1974 if (expr.delegate) {
1979 if (expr.argument) {
1982 this.generateExpression(expr.argument, Precedence.Yield, E_TTT)
1985 return parenthesize(result, Precedence.Yield, precedence);
1988 AwaitExpression: function (expr, precedence, flags) {
1990 expr.all ? 'await*' : 'await',
1991 this.generateExpression(expr.argument, Precedence.Await, E_TTT)
1993 return parenthesize(result, Precedence.Await, precedence);
1996 UpdateExpression: function (expr, precedence, flags) {
1998 return parenthesize(
2001 this.generateExpression(expr.argument, Precedence.Unary, E_TTT)
2007 return parenthesize(
2009 this.generateExpression(expr.argument, Precedence.Postfix, E_TTT),
2017 FunctionExpression: function (expr, precedence, flags) {
2019 generateAsyncPrefix(expr, true),
2023 result.push(generateStarSuffix(expr) || noEmptySpace());
2024 result.push(generateIdentifier(expr.id));
2026 result.push(generateStarSuffix(expr) || space);
2028 result.push(this.generateFunctionBody(expr));
2032 ExportBatchSpecifier: function (expr, precedence, flags) {
2036 ArrayPattern: function (expr, precedence, flags) {
2037 return this.ArrayExpression(expr, precedence, flags, true);
2040 ArrayExpression: function (expr, precedence, flags, isPattern) {
2041 var result, multiline, that = this;
2042 if (!expr.elements.length) {
2045 multiline = isPattern ? false : expr.elements.length > 1;
2046 result = ['[', multiline ? newline : ''];
2047 withIndent(function (indent) {
2049 for (i = 0, iz = expr.elements.length; i < iz; ++i) {
2050 if (!expr.elements[i]) {
2052 result.push(indent);
2058 result.push(multiline ? indent : '');
2059 result.push(that.generateExpression(expr.elements[i], Precedence.Assignment, E_TTT));
2062 result.push(',' + (multiline ? newline : space));
2066 if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2067 result.push(newline);
2069 result.push(multiline ? base : '');
2074 RestElement: function(expr, precedence, flags) {
2075 return '...' + this.generatePattern(expr.argument);
2078 ClassExpression: function (expr, precedence, flags) {
2079 var result, fragment;
2082 result = join(result, this.generateExpression(expr.id, Precedence.Sequence, E_TTT));
2084 if (expr.superClass) {
2085 fragment = join('extends', this.generateExpression(expr.superClass, Precedence.Assignment, E_TTT));
2086 result = join(result, fragment);
2089 result.push(this.generateStatement(expr.body, S_TFFT));
2093 MethodDefinition: function (expr, precedence, flags) {
2094 var result, fragment;
2095 if (expr['static']) {
2096 result = ['static' + space];
2100 if (expr.kind === 'get' || expr.kind === 'set') {
2102 join(expr.kind, this.generatePropertyKey(expr.key, expr.computed)),
2103 this.generateFunctionBody(expr.value)
2107 generateMethodPrefix(expr),
2108 this.generatePropertyKey(expr.key, expr.computed),
2109 this.generateFunctionBody(expr.value)
2112 return join(result, fragment);
2115 Property: function (expr, precedence, flags) {
2116 if (expr.kind === 'get' || expr.kind === 'set') {
2118 expr.kind, noEmptySpace(),
2119 this.generatePropertyKey(expr.key, expr.computed),
2120 this.generateFunctionBody(expr.value)
2124 if (expr.shorthand) {
2125 return this.generatePropertyKey(expr.key, expr.computed);
2130 generateMethodPrefix(expr),
2131 this.generatePropertyKey(expr.key, expr.computed),
2132 this.generateFunctionBody(expr.value)
2137 this.generatePropertyKey(expr.key, expr.computed),
2139 this.generateExpression(expr.value, Precedence.Assignment, E_TTT)
2143 ObjectExpression: function (expr, precedence, flags) {
2144 var multiline, result, fragment, that = this;
2146 if (!expr.properties.length) {
2149 multiline = expr.properties.length > 1;
2151 withIndent(function () {
2152 fragment = that.generateExpression(expr.properties[0], Precedence.Sequence, E_TTT);
2157 // Do not transform from
2158 // dejavu.Class.declare({
2159 // method2: function () {}
2162 // dejavu.Class.declare({method2: function () {
2164 if (!hasLineTerminator(toSourceNodeWhenNeeded(fragment).toString())) {
2165 return [ '{', space, fragment, space, '}' ];
2169 withIndent(function (indent) {
2171 result = [ '{', newline, indent, fragment ];
2174 result.push(',' + newline);
2175 for (i = 1, iz = expr.properties.length; i < iz; ++i) {
2176 result.push(indent);
2177 result.push(that.generateExpression(expr.properties[i], Precedence.Sequence, E_TTT));
2179 result.push(',' + newline);
2185 if (!endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2186 result.push(newline);
2193 ObjectPattern: function (expr, precedence, flags) {
2194 var result, i, iz, multiline, property, that = this;
2195 if (!expr.properties.length) {
2200 if (expr.properties.length === 1) {
2201 property = expr.properties[0];
2202 if (property.value.type !== Syntax.Identifier) {
2206 for (i = 0, iz = expr.properties.length; i < iz; ++i) {
2207 property = expr.properties[i];
2208 if (!property.shorthand) {
2214 result = ['{', multiline ? newline : '' ];
2216 withIndent(function (indent) {
2218 for (i = 0, iz = expr.properties.length; i < iz; ++i) {
2219 result.push(multiline ? indent : '');
2220 result.push(that.generateExpression(expr.properties[i], Precedence.Sequence, E_TTT));
2222 result.push(',' + (multiline ? newline : space));
2227 if (multiline && !endsWithLineTerminator(toSourceNodeWhenNeeded(result).toString())) {
2228 result.push(newline);
2230 result.push(multiline ? base : '');
2235 ThisExpression: function (expr, precedence, flags) {
2239 Super: function (expr, precedence, flags) {
2243 Identifier: function (expr, precedence, flags) {
2244 return generateIdentifier(expr);
2247 ImportDefaultSpecifier: function (expr, precedence, flags) {
2248 return generateIdentifier(expr.id || expr.local);
2251 ImportNamespaceSpecifier: function (expr, precedence, flags) {
2253 var id = expr.id || expr.local;
2255 result.push(space + 'as' + noEmptySpace() + generateIdentifier(id));
2260 ImportSpecifier: function (expr, precedence, flags) {
2261 return this.ExportSpecifier(expr, precedence, flags);
2264 ExportSpecifier: function (expr, precedence, flags) {
2265 var exported = (expr.id || expr.imported).name;
2266 var result = [ exported ];
2267 var id = expr.name || expr.local;
2268 if (id && id.name !== exported) {
2269 result.push(noEmptySpace() + 'as' + noEmptySpace() + generateIdentifier(id));
2274 Literal: function (expr, precedence, flags) {
2276 if (expr.hasOwnProperty('raw') && parse && extra.raw) {
2278 raw = parse(expr.raw).body[0].expression;
2279 if (raw.type === Syntax.Literal) {
2280 if (raw.value === expr.value) {
2285 // not use raw property
2289 if (expr.value === null) {
2293 if (typeof expr.value === 'string') {
2294 return escapeString(expr.value);
2297 if (typeof expr.value === 'number') {
2298 return generateNumber(expr.value);
2301 if (typeof expr.value === 'boolean') {
2302 return expr.value ? 'true' : 'false';
2305 return generateRegExp(expr.value);
2308 GeneratorExpression: function (expr, precedence, flags) {
2309 return this.ComprehensionExpression(expr, precedence, flags);
2312 ComprehensionExpression: function (expr, precedence, flags) {
2313 // GeneratorExpression should be parenthesized with (...), ComprehensionExpression with [...]
2314 // Due to https://bugzilla.mozilla.org/show_bug.cgi?id=883468 position of expr.body can differ in Spidermonkey and ES6
2316 var result, i, iz, fragment, that = this;
2317 result = (expr.type === Syntax.GeneratorExpression) ? ['('] : ['['];
2319 if (extra.moz.comprehensionExpressionStartsWithAssignment) {
2320 fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
2321 result.push(fragment);
2325 withIndent(function () {
2326 for (i = 0, iz = expr.blocks.length; i < iz; ++i) {
2327 fragment = that.generateExpression(expr.blocks[i], Precedence.Sequence, E_TTT);
2328 if (i > 0 || extra.moz.comprehensionExpressionStartsWithAssignment) {
2329 result = join(result, fragment);
2331 result.push(fragment);
2338 result = join(result, 'if' + space);
2339 fragment = this.generateExpression(expr.filter, Precedence.Sequence, E_TTT);
2340 result = join(result, [ '(', fragment, ')' ]);
2343 if (!extra.moz.comprehensionExpressionStartsWithAssignment) {
2344 fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
2346 result = join(result, fragment);
2349 result.push((expr.type === Syntax.GeneratorExpression) ? ')' : ']');
2353 ComprehensionBlock: function (expr, precedence, flags) {
2355 if (expr.left.type === Syntax.VariableDeclaration) {
2357 expr.left.kind, noEmptySpace(),
2358 this.generateStatement(expr.left.declarations[0], S_FFFF)
2361 fragment = this.generateExpression(expr.left, Precedence.Call, E_TTT);
2364 fragment = join(fragment, expr.of ? 'of' : 'in');
2365 fragment = join(fragment, this.generateExpression(expr.right, Precedence.Sequence, E_TTT));
2367 return [ 'for' + space + '(', fragment, ')' ];
2370 SpreadElement: function (expr, precedence, flags) {
2373 this.generateExpression(expr.argument, Precedence.Assignment, E_TTT)
2377 TaggedTemplateExpression: function (expr, precedence, flags) {
2378 var itemFlags = E_TTF;
2379 if (!(flags & F_ALLOW_CALL)) {
2383 this.generateExpression(expr.tag, Precedence.Call, itemFlags),
2384 this.generateExpression(expr.quasi, Precedence.Primary, E_FFT)
2386 return parenthesize(result, Precedence.TaggedTemplate, precedence);
2389 TemplateElement: function (expr, precedence, flags) {
2390 // Don't use "cooked". Since tagged template can use raw template
2391 // representation. So if we do so, it breaks the script semantics.
2392 return expr.value.raw;
2395 TemplateLiteral: function (expr, precedence, flags) {
2398 for (i = 0, iz = expr.quasis.length; i < iz; ++i) {
2399 result.push(this.generateExpression(expr.quasis[i], Precedence.Primary, E_TTT));
2401 result.push('${' + space);
2402 result.push(this.generateExpression(expr.expressions[i], Precedence.Sequence, E_TTT));
2403 result.push(space + '}');
2410 ModuleSpecifier: function (expr, precedence, flags) {
2411 return this.Literal(expr, precedence, flags);
2416 merge(CodeGenerator.prototype, CodeGenerator.Expression);
2418 CodeGenerator.prototype.generateExpression = function (expr, precedence, flags) {
2421 type = expr.type || Syntax.Property;
2423 if (extra.verbatim && expr.hasOwnProperty(extra.verbatim)) {
2424 return generateVerbatim(expr, precedence);
2427 result = this[type](expr, precedence, flags);
2430 if (extra.comment) {
2431 result = addComments(expr, result);
2433 return toSourceNodeWhenNeeded(result, expr);
2436 CodeGenerator.prototype.generateStatement = function (stmt, flags) {
2440 result = this[stmt.type](stmt, flags);
2444 if (extra.comment) {
2445 result = addComments(stmt, result);
2448 fragment = toSourceNodeWhenNeeded(result).toString();
2449 if (stmt.type === Syntax.Program && !safeConcatenation && newline === '' && fragment.charAt(fragment.length - 1) === '\n') {
2450 result = sourceMap ? toSourceNodeWhenNeeded(result).replaceRight(/\s+$/, '') : fragment.replace(/\s+$/, '');
2453 return toSourceNodeWhenNeeded(result, stmt);
2456 function generateInternal(node) {
2459 codegen = new CodeGenerator();
2460 if (isStatement(node)) {
2461 return codegen.generateStatement(node, S_TFFF);
2464 if (isExpression(node)) {
2465 return codegen.generateExpression(node, Precedence.Sequence, E_TTT);
2468 throw new Error('Unknown node type: ' + node.type);
2471 function generate(node, options) {
2472 var defaultOptions = getDefaultOptions(), result, pair;
2474 if (options != null) {
2480 // Instead of them, we can use `option.format.indent`.
2481 if (typeof options.indent === 'string') {
2482 defaultOptions.format.indent.style = options.indent;
2484 if (typeof options.base === 'number') {
2485 defaultOptions.format.indent.base = options.base;
2487 options = updateDeeply(defaultOptions, options);
2488 indent = options.format.indent.style;
2489 if (typeof options.base === 'string') {
2490 base = options.base;
2492 base = stringRepeat(indent, options.format.indent.base);
2495 options = defaultOptions;
2496 indent = options.format.indent.style;
2497 base = stringRepeat(indent, options.format.indent.base);
2499 json = options.format.json;
2500 renumber = options.format.renumber;
2501 hexadecimal = json ? false : options.format.hexadecimal;
2502 quotes = json ? 'double' : options.format.quotes;
2503 escapeless = options.format.escapeless;
2504 newline = options.format.newline;
2505 space = options.format.space;
2506 if (options.format.compact) {
2507 newline = space = indent = base = '';
2509 parentheses = options.format.parentheses;
2510 semicolons = options.format.semicolons;
2511 safeConcatenation = options.format.safeConcatenation;
2512 directive = options.directive;
2513 parse = json ? null : options.parse;
2514 sourceMap = options.sourceMap;
2515 sourceCode = options.sourceCode;
2516 preserveBlankLines = options.format.preserveBlankLines && sourceCode !== null;
2520 if (!exports.browser) {
2521 // We assume environment is node.js
2522 // And prevent from including source-map by browserify
2523 SourceNode = require('source-map').SourceNode;
2525 SourceNode = global.sourceMap.SourceNode;
2529 result = generateInternal(node);
2532 pair = {code: result.toString(), map: null};
2533 return options.sourceMapWithCode ? pair : pair.code;
2537 pair = result.toStringWithSourceMap({
2539 sourceRoot: options.sourceMapRoot
2542 if (options.sourceContent) {
2543 pair.map.setSourceContent(options.sourceMap,
2544 options.sourceContent);
2547 if (options.sourceMapWithCode) {
2551 return pair.map.toString();
2568 FORMAT_DEFAULTS = getDefaultOptions().format;
2570 exports.version = require('./package.json').version;
2571 exports.generate = generate;
2572 exports.attachComments = estraverse.attachComments;
2573 exports.Precedence = updateDeeply({}, Precedence);
2574 exports.browser = false;
2575 exports.FORMAT_MINIFY = FORMAT_MINIFY;
2576 exports.FORMAT_DEFAULTS = FORMAT_DEFAULTS;
2578 /* vim: set sw=4 ts=4 et tw=80 : */