/** * Copyright 2013 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*jslint node:true*/ /** * Desugars ES6 rest parameters into an ES3 arguments array. * * function printf(template, ...args) { * args.forEach(...); * } * * We could use `Array.prototype.slice.call`, but that usage of arguments causes * functions to be deoptimized in V8, so instead we use a for-loop. * * function printf(template) { * for (var args = [], $__0 = 1, $__1 = arguments.length; $__0 < $__1; $__0++) * args.push(arguments[$__0]); * args.forEach(...); * } * */ var Syntax = require('esprima-fb').Syntax; var utils = require('../src/utils'); function _nodeIsFunctionWithRestParam(node) { return (node.type === Syntax.FunctionDeclaration || node.type === Syntax.FunctionExpression || node.type === Syntax.ArrowFunctionExpression) && node.rest; } function visitFunctionParamsWithRestParam(traverse, node, path, state) { if (node.parametricType) { utils.catchup(node.parametricType.range[0], state); path.unshift(node); traverse(node.parametricType, path, state); path.shift(); } // Render params. if (node.params.length) { path.unshift(node); traverse(node.params, path, state); path.shift(); } else { // -3 is for ... of the rest. utils.catchup(node.rest.range[0] - 3, state); } utils.catchupWhiteSpace(node.rest.range[1], state); path.unshift(node); traverse(node.body, path, state); path.shift(); return false; } visitFunctionParamsWithRestParam.test = function(node, path, state) { return _nodeIsFunctionWithRestParam(node); }; function renderRestParamSetup(functionNode, state) { var idx = state.localScope.tempVarIndex++; var len = state.localScope.tempVarIndex++; return 'for (var ' + functionNode.rest.name + '=[],' + utils.getTempVar(idx) + '=' + functionNode.params.length + ',' + utils.getTempVar(len) + '=arguments.length;' + utils.getTempVar(idx) + '<' + utils.getTempVar(len) + ';' + utils.getTempVar(idx) + '++) ' + functionNode.rest.name + '.push(arguments[' + utils.getTempVar(idx) + ']);'; } function visitFunctionBodyWithRestParam(traverse, node, path, state) { utils.catchup(node.range[0] + 1, state); var parentNode = path[0]; utils.append(renderRestParamSetup(parentNode, state), state); return true; } visitFunctionBodyWithRestParam.test = function(node, path, state) { return node.type === Syntax.BlockStatement && _nodeIsFunctionWithRestParam(path[0]); }; exports.renderRestParamSetup = renderRestParamSetup; exports.visitorList = [ visitFunctionParamsWithRestParam, visitFunctionBodyWithRestParam ];