es6-rest-param-visitors.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /**
  2. * Copyright 2013-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. /*jslint node:true*/
  10. /**
  11. * Desugars ES6 rest parameters into an ES3 arguments array.
  12. *
  13. * function printf(template, ...args) {
  14. * args.forEach(...);
  15. * }
  16. *
  17. * We could use `Array.prototype.slice.call`, but that usage of arguments causes
  18. * functions to be deoptimized in V8, so instead we use a for-loop.
  19. *
  20. * function printf(template) {
  21. * for (var args = [], $__0 = 1, $__1 = arguments.length; $__0 < $__1; $__0++)
  22. * args.push(arguments[$__0]);
  23. * args.forEach(...);
  24. * }
  25. *
  26. */
  27. var Syntax = require('esprima-fb').Syntax;
  28. var utils = require('../src/utils');
  29. function _nodeIsFunctionWithRestParam(node) {
  30. return (node.type === Syntax.FunctionDeclaration
  31. || node.type === Syntax.FunctionExpression
  32. || node.type === Syntax.ArrowFunctionExpression)
  33. && node.rest;
  34. }
  35. function visitFunctionParamsWithRestParam(traverse, node, path, state) {
  36. if (node.parametricType) {
  37. utils.catchup(node.parametricType.range[0], state);
  38. path.unshift(node);
  39. traverse(node.parametricType, path, state);
  40. path.shift();
  41. }
  42. // Render params.
  43. if (node.params.length) {
  44. path.unshift(node);
  45. traverse(node.params, path, state);
  46. path.shift();
  47. } else {
  48. // -3 is for ... of the rest.
  49. utils.catchup(node.rest.range[0] - 3, state);
  50. }
  51. utils.catchupWhiteSpace(node.rest.range[1], state);
  52. utils.catchup(node.body.range[0], state);
  53. path.unshift(node);
  54. traverse(node.body, path, state);
  55. path.shift();
  56. return false;
  57. }
  58. visitFunctionParamsWithRestParam.test = function(node, path, state) {
  59. return _nodeIsFunctionWithRestParam(node);
  60. };
  61. function renderRestParamSetup(functionNode, state) {
  62. var idx = state.localScope.tempVarIndex++;
  63. var len = state.localScope.tempVarIndex++;
  64. return 'for (var ' + functionNode.rest.name + '=[],' +
  65. utils.getTempVar(idx) + '=' + functionNode.params.length + ',' +
  66. utils.getTempVar(len) + '=arguments.length;' +
  67. utils.getTempVar(idx) + '<' + utils.getTempVar(len) + ';' +
  68. utils.getTempVar(idx) + '++) ' +
  69. functionNode.rest.name + '.push(arguments[' + utils.getTempVar(idx) + ']);';
  70. }
  71. function visitFunctionBodyWithRestParam(traverse, node, path, state) {
  72. utils.catchup(node.range[0] + 1, state);
  73. var parentNode = path[0];
  74. utils.append(renderRestParamSetup(parentNode, state), state);
  75. return true;
  76. }
  77. visitFunctionBodyWithRestParam.test = function(node, path, state) {
  78. return node.type === Syntax.BlockStatement
  79. && _nodeIsFunctionWithRestParam(path[0]);
  80. };
  81. exports.renderRestParamSetup = renderRestParamSetup;
  82. exports.visitorList = [
  83. visitFunctionParamsWithRestParam,
  84. visitFunctionBodyWithRestParam
  85. ];