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