123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- /**
- * Copyright 2013-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
- /*global exports:true*/
- /**
- * Implements ES6 destructuring assignment and pattern matchng.
- *
- * function init({port, ip, coords: [x, y]}) {
- * return (x && y) ? {id, port} : {ip};
- * };
- *
- * function init($__0) {
- * var
- * port = $__0.port,
- * ip = $__0.ip,
- * $__1 = $__0.coords,
- * x = $__1[0],
- * y = $__1[1];
- * return (x && y) ? {id, port} : {ip};
- * }
- *
- * var x, {ip, port} = init({ip, port});
- *
- * var x, $__0 = init({ip, port}), ip = $__0.ip, port = $__0.port;
- *
- */
- var Syntax = require('esprima-fb').Syntax;
- var utils = require('../src/utils');
- var reservedWordsHelper = require('./reserved-words-helper');
- var restParamVisitors = require('./es6-rest-param-visitors');
- var restPropertyHelpers = require('./es7-rest-property-helpers');
- // -------------------------------------------------------
- // 1. Structured variable declarations.
- //
- // var [a, b] = [b, a];
- // var {x, y} = {y, x};
- // -------------------------------------------------------
- function visitStructuredVariable(traverse, node, path, state) {
- // Allocate new temp for the pattern.
- utils.append(utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
- // Skip the pattern and assign the init to the temp.
- utils.catchupWhiteSpace(node.init.range[0], state);
- traverse(node.init, path, state);
- utils.catchup(node.init.range[1], state);
- // Render the destructured data.
- utils.append(',' + getDestructuredComponents(node.id, state), state);
- state.localScope.tempVarIndex++;
- return false;
- }
- visitStructuredVariable.test = function(node, path, state) {
- return node.type === Syntax.VariableDeclarator &&
- isStructuredPattern(node.id);
- };
- function isStructuredPattern(node) {
- return node.type === Syntax.ObjectPattern ||
- node.type === Syntax.ArrayPattern;
- }
- // Main function which does actual recursive destructuring
- // of nested complex structures.
- function getDestructuredComponents(node, state) {
- var tmpIndex = state.localScope.tempVarIndex;
- var components = [];
- var patternItems = getPatternItems(node);
- for (var idx = 0; idx < patternItems.length; idx++) {
- var item = patternItems[idx];
- if (!item) {
- continue;
- }
- if (item.type === Syntax.SpreadElement) {
- // Spread/rest of an array.
- // TODO(dmitrys): support spread in the middle of a pattern
- // and also for function param patterns: [x, ...xs, y]
- components.push(item.argument.name +
- '=Array.prototype.slice.call(' +
- utils.getTempVar(tmpIndex) + ',' + idx + ')'
- );
- continue;
- }
- if (item.type === Syntax.SpreadProperty) {
- var restExpression = restPropertyHelpers.renderRestExpression(
- utils.getTempVar(tmpIndex),
- patternItems
- );
- components.push(item.argument.name + '=' + restExpression);
- continue;
- }
- // Depending on pattern type (Array or Object), we get
- // corresponding pattern item parts.
- var accessor = getPatternItemAccessor(node, item, tmpIndex, idx);
- var value = getPatternItemValue(node, item);
- // TODO(dmitrys): implement default values: {x, y=5}
- if (value.type === Syntax.Identifier) {
- // Simple pattern item.
- components.push(value.name + '=' + accessor);
- } else {
- // Complex sub-structure.
- components.push(
- utils.getTempVar(++state.localScope.tempVarIndex) + '=' + accessor +
- ',' + getDestructuredComponents(value, state)
- );
- }
- }
- return components.join(',');
- }
- function getPatternItems(node) {
- return node.properties || node.elements;
- }
- function getPatternItemAccessor(node, patternItem, tmpIndex, idx) {
- var tmpName = utils.getTempVar(tmpIndex);
- if (node.type === Syntax.ObjectPattern) {
- if (reservedWordsHelper.isReservedWord(patternItem.key.name)) {
- return tmpName + '["' + patternItem.key.name + '"]';
- } else if (patternItem.key.type === Syntax.Literal) {
- return tmpName + '[' + JSON.stringify(patternItem.key.value) + ']';
- } else if (patternItem.key.type === Syntax.Identifier) {
- return tmpName + '.' + patternItem.key.name;
- }
- } else if (node.type === Syntax.ArrayPattern) {
- return tmpName + '[' + idx + ']';
- }
- }
- function getPatternItemValue(node, patternItem) {
- return node.type === Syntax.ObjectPattern
- ? patternItem.value
- : patternItem;
- }
- // -------------------------------------------------------
- // 2. Assignment expression.
- //
- // [a, b] = [b, a];
- // ({x, y} = {y, x});
- // -------------------------------------------------------
- function visitStructuredAssignment(traverse, node, path, state) {
- var exprNode = node.expression;
- utils.append('var ' + utils.getTempVar(state.localScope.tempVarIndex) + '=', state);
- utils.catchupWhiteSpace(exprNode.right.range[0], state);
- traverse(exprNode.right, path, state);
- utils.catchup(exprNode.right.range[1], state);
- utils.append(
- ';' + getDestructuredComponents(exprNode.left, state) + ';',
- state
- );
- utils.catchupWhiteSpace(node.range[1], state);
- state.localScope.tempVarIndex++;
- return false;
- }
- visitStructuredAssignment.test = function(node, path, state) {
- // We consider the expression statement rather than just assignment
- // expression to cover case with object patters which should be
- // wrapped in grouping operator: ({x, y} = {y, x});
- return node.type === Syntax.ExpressionStatement &&
- node.expression.type === Syntax.AssignmentExpression &&
- isStructuredPattern(node.expression.left);
- };
- // -------------------------------------------------------
- // 3. Structured parameter.
- //
- // function foo({x, y}) { ... }
- // -------------------------------------------------------
- function visitStructuredParameter(traverse, node, path, state) {
- utils.append(utils.getTempVar(getParamIndex(node, path)), state);
- utils.catchupWhiteSpace(node.range[1], state);
- return true;
- }
- function getParamIndex(paramNode, path) {
- var funcNode = path[0];
- var tmpIndex = 0;
- for (var k = 0; k < funcNode.params.length; k++) {
- var param = funcNode.params[k];
- if (param === paramNode) {
- break;
- }
- if (isStructuredPattern(param)) {
- tmpIndex++;
- }
- }
- return tmpIndex;
- }
- visitStructuredParameter.test = function(node, path, state) {
- return isStructuredPattern(node) && isFunctionNode(path[0]);
- };
- function isFunctionNode(node) {
- return (node.type == Syntax.FunctionDeclaration ||
- node.type == Syntax.FunctionExpression ||
- node.type == Syntax.MethodDefinition ||
- node.type == Syntax.ArrowFunctionExpression);
- }
- // -------------------------------------------------------
- // 4. Function body for structured parameters.
- //
- // function foo({x, y}) { x; y; }
- // -------------------------------------------------------
- function visitFunctionBodyForStructuredParameter(traverse, node, path, state) {
- var funcNode = path[0];
- utils.catchup(funcNode.body.range[0] + 1, state);
- renderDestructuredComponents(funcNode, state);
- if (funcNode.rest) {
- utils.append(
- restParamVisitors.renderRestParamSetup(funcNode, state),
- state
- );
- }
- return true;
- }
- function renderDestructuredComponents(funcNode, state) {
- var destructuredComponents = [];
- for (var k = 0; k < funcNode.params.length; k++) {
- var param = funcNode.params[k];
- if (isStructuredPattern(param)) {
- destructuredComponents.push(
- getDestructuredComponents(param, state)
- );
- state.localScope.tempVarIndex++;
- }
- }
- if (destructuredComponents.length) {
- utils.append('var ' + destructuredComponents.join(',') + ';', state);
- }
- }
- visitFunctionBodyForStructuredParameter.test = function(node, path, state) {
- return node.type === Syntax.BlockStatement && isFunctionNode(path[0]);
- };
- exports.visitorList = [
- visitStructuredVariable,
- visitStructuredAssignment,
- visitStructuredParameter,
- visitFunctionBodyForStructuredParameter
- ];
- exports.renderDestructuredComponents = renderDestructuredComponents;
|