es6-destructuring-visitors.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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. /*global exports:true*/
  10. /**
  11. * Implements ES6 destructuring assignment and pattern matchng.
  12. *
  13. * function init({port, ip, coords: [x, y]}) {
  14. * return (x && y) ? {id, port} : {ip};
  15. * };
  16. *
  17. * function init($__0) {
  18. * var
  19. * port = $__0.port,
  20. * ip = $__0.ip,
  21. * $__1 = $__0.coords,
  22. * x = $__1[0],
  23. * y = $__1[1];
  24. * return (x && y) ? {id, port} : {ip};
  25. * }
  26. *
  27. * var x, {ip, port} = init({ip, port});
  28. *
  29. * var x, $__0 = init({ip, port}), ip = $__0.ip, port = $__0.port;
  30. *
  31. */
  32. var Syntax = require('esprima-fb').Syntax;
  33. var utils = require('../src/utils');
  34. var reservedWordsHelper = require('./reserved-words-helper');
  35. var restParamVisitors = require('./es6-rest-param-visitors');
  36. var restPropertyHelpers = require('./es7-rest-property-helpers');
  37. // -------------------------------------------------------
  38. // 1. Structured variable declarations.
  39. //
  40. // var [a, b] = [b, a];
  41. // var {x, y} = {y, x};
  42. // -------------------------------------------------------
  43. function visitStructuredVariable(traverse, node, path, state) {
  44. // Allocate new temp for the pattern.
  45. utils.append(utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
  46. // Skip the pattern and assign the init to the temp.
  47. utils.catchupWhiteSpace(node.init.range[0], state);
  48. traverse(node.init, path, state);
  49. utils.catchup(node.init.range[1], state);
  50. // Render the destructured data.
  51. utils.append(',' + getDestructuredComponents(node.id, state), state);
  52. state.localScope.tempVarIndex++;
  53. return false;
  54. }
  55. visitStructuredVariable.test = function(node, path, state) {
  56. return node.type === Syntax.VariableDeclarator &&
  57. isStructuredPattern(node.id);
  58. };
  59. function isStructuredPattern(node) {
  60. return node.type === Syntax.ObjectPattern ||
  61. node.type === Syntax.ArrayPattern;
  62. }
  63. // Main function which does actual recursive destructuring
  64. // of nested complex structures.
  65. function getDestructuredComponents(node, state) {
  66. var tmpIndex = state.localScope.tempVarIndex;
  67. var components = [];
  68. var patternItems = getPatternItems(node);
  69. for (var idx = 0; idx < patternItems.length; idx++) {
  70. var item = patternItems[idx];
  71. if (!item) {
  72. continue;
  73. }
  74. if (item.type === Syntax.SpreadElement) {
  75. // Spread/rest of an array.
  76. // TODO(dmitrys): support spread in the middle of a pattern
  77. // and also for function param patterns: [x, ...xs, y]
  78. components.push(item.argument.name +
  79. '=Array.prototype.slice.call(' +
  80. utils.getTempVar(tmpIndex) + ',' + idx + ')'
  81. );
  82. continue;
  83. }
  84. if (item.type === Syntax.SpreadProperty) {
  85. var restExpression = restPropertyHelpers.renderRestExpression(
  86. utils.getTempVar(tmpIndex),
  87. patternItems
  88. );
  89. components.push(item.argument.name + '=' + restExpression);
  90. continue;
  91. }
  92. // Depending on pattern type (Array or Object), we get
  93. // corresponding pattern item parts.
  94. var accessor = getPatternItemAccessor(node, item, tmpIndex, idx);
  95. var value = getPatternItemValue(node, item);
  96. // TODO(dmitrys): implement default values: {x, y=5}
  97. if (value.type === Syntax.Identifier) {
  98. // Simple pattern item.
  99. components.push(value.name + '=' + accessor);
  100. } else {
  101. // Complex sub-structure.
  102. components.push(
  103. utils.getTempVar(++state.localScope.tempVarIndex) + '=' + accessor +
  104. ',' + getDestructuredComponents(value, state)
  105. );
  106. }
  107. }
  108. return components.join(',');
  109. }
  110. function getPatternItems(node) {
  111. return node.properties || node.elements;
  112. }
  113. function getPatternItemAccessor(node, patternItem, tmpIndex, idx) {
  114. var tmpName = utils.getTempVar(tmpIndex);
  115. if (node.type === Syntax.ObjectPattern) {
  116. if (reservedWordsHelper.isReservedWord(patternItem.key.name)) {
  117. return tmpName + '["' + patternItem.key.name + '"]';
  118. } else if (patternItem.key.type === Syntax.Literal) {
  119. return tmpName + '[' + JSON.stringify(patternItem.key.value) + ']';
  120. } else if (patternItem.key.type === Syntax.Identifier) {
  121. return tmpName + '.' + patternItem.key.name;
  122. }
  123. } else if (node.type === Syntax.ArrayPattern) {
  124. return tmpName + '[' + idx + ']';
  125. }
  126. }
  127. function getPatternItemValue(node, patternItem) {
  128. return node.type === Syntax.ObjectPattern
  129. ? patternItem.value
  130. : patternItem;
  131. }
  132. // -------------------------------------------------------
  133. // 2. Assignment expression.
  134. //
  135. // [a, b] = [b, a];
  136. // ({x, y} = {y, x});
  137. // -------------------------------------------------------
  138. function visitStructuredAssignment(traverse, node, path, state) {
  139. var exprNode = node.expression;
  140. utils.append('var ' + utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
  141. utils.catchupWhiteSpace(exprNode.right.range[0], state);
  142. traverse(exprNode.right, path, state);
  143. utils.catchup(exprNode.right.range[1], state);
  144. utils.append(
  145. ';' + getDestructuredComponents(exprNode.left, state) + ';',
  146. state
  147. );
  148. utils.catchupWhiteSpace(node.range[1], state);
  149. state.localScope.tempVarIndex++;
  150. return false;
  151. }
  152. visitStructuredAssignment.test = function(node, path, state) {
  153. // We consider the expression statement rather than just assignment
  154. // expression to cover case with object patters which should be
  155. // wrapped in grouping operator: ({x, y} = {y, x});
  156. return node.type === Syntax.ExpressionStatement &&
  157. node.expression.type === Syntax.AssignmentExpression &&
  158. isStructuredPattern(node.expression.left);
  159. };
  160. // -------------------------------------------------------
  161. // 3. Structured parameter.
  162. //
  163. // function foo({x, y}) { ... }
  164. // -------------------------------------------------------
  165. function visitStructuredParameter(traverse, node, path, state) {
  166. utils.append(utils.getTempVar(getParamIndex(node, path)), state);
  167. utils.catchupWhiteSpace(node.range[1], state);
  168. return true;
  169. }
  170. function getParamIndex(paramNode, path) {
  171. var funcNode = path[0];
  172. var tmpIndex = 0;
  173. for (var k = 0; k < funcNode.params.length; k++) {
  174. var param = funcNode.params[k];
  175. if (param === paramNode) {
  176. break;
  177. }
  178. if (isStructuredPattern(param)) {
  179. tmpIndex++;
  180. }
  181. }
  182. return tmpIndex;
  183. }
  184. visitStructuredParameter.test = function(node, path, state) {
  185. return isStructuredPattern(node) && isFunctionNode(path[0]);
  186. };
  187. function isFunctionNode(node) {
  188. return (node.type == Syntax.FunctionDeclaration ||
  189. node.type == Syntax.FunctionExpression ||
  190. node.type == Syntax.MethodDefinition ||
  191. node.type == Syntax.ArrowFunctionExpression);
  192. }
  193. // -------------------------------------------------------
  194. // 4. Function body for structured parameters.
  195. //
  196. // function foo({x, y}) { x; y; }
  197. // -------------------------------------------------------
  198. function visitFunctionBodyForStructuredParameter(traverse, node, path, state) {
  199. var funcNode = path[0];
  200. utils.catchup(funcNode.body.range[0] + 1, state);
  201. renderDestructuredComponents(funcNode, state);
  202. if (funcNode.rest) {
  203. utils.append(
  204. restParamVisitors.renderRestParamSetup(funcNode, state),
  205. state
  206. );
  207. }
  208. return true;
  209. }
  210. function renderDestructuredComponents(funcNode, state) {
  211. var destructuredComponents = [];
  212. for (var k = 0; k < funcNode.params.length; k++) {
  213. var param = funcNode.params[k];
  214. if (isStructuredPattern(param)) {
  215. destructuredComponents.push(
  216. getDestructuredComponents(param, state)
  217. );
  218. state.localScope.tempVarIndex++;
  219. }
  220. }
  221. if (destructuredComponents.length) {
  222. utils.append('var ' + destructuredComponents.join(',') + ';', state);
  223. }
  224. }
  225. visitFunctionBodyForStructuredParameter.test = function(node, path, state) {
  226. return node.type === Syntax.BlockStatement && isFunctionNode(path[0]);
  227. };
  228. exports.visitorList = [
  229. visitStructuredVariable,
  230. visitStructuredAssignment,
  231. visitStructuredParameter,
  232. visitFunctionBodyForStructuredParameter
  233. ];
  234. exports.renderDestructuredComponents = renderDestructuredComponents;