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 : */