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