UNPKG

babel-core

Version:

Turn ES6 code into readable vanilla ES5 with source maps

149 lines (119 loc) 4.61 kB
"use strict"; var _interopRequireWildcard = function (obj) { return obj && obj.__esModule ? obj : { "default": obj }; }; var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; exports.__esModule = true; var isNumber = _interopRequire(require("lodash/lang/isNumber")); var util = _interopRequireWildcard(require("../../../util")); var t = _interopRequireWildcard(require("../../../types")); var check = t.isRestElement; exports.check = check; var memberExpressionOptimisationVisitor = { enter: function enter(node, parent, scope, state) { // check if this scope has a local binding that will shadow the rest parameter if (this.isScope() && !scope.bindingIdentifierEquals(state.name, state.outerBinding)) { return this.skip(); } // skip over functions as whatever `arguments` we reference inside will refer // to the wrong function if (this.isFunctionDeclaration() || this.isFunctionExpression()) { state.noOptimise = true; this.traverse(memberExpressionOptimisationVisitor, state); state.noOptimise = false; return this.skip(); } // is this a referenced identifier and is it referencing the rest parameter? if (!this.isReferencedIdentifier({ name: state.name })) return; if (!state.noOptimise && t.isMemberExpression(parent) && parent.computed) { // if we know that this member expression is referencing a number then we can safely // optimise it var prop = parent.property; if (isNumber(prop.value) || t.isUnaryExpression(prop) || t.isBinaryExpression(prop)) { state.candidates.push(this); return; } } state.canOptimise = false; this.stop(); } }; function optimizeMemberExpression(parent, offset) { var newExpr; var prop = parent.property; if (t.isLiteral(prop)) { prop.value += offset; prop.raw = String(prop.value); } else { // // UnaryExpression, BinaryExpression newExpr = t.binaryExpression("+", prop, t.literal(offset)); parent.property = newExpr; } } var hasRest = function hasRest(node) { return t.isRestElement(node.params[node.params.length - 1]); }; exports.Function = function (node, parent, scope, file) { if (!hasRest(node)) return; var rest = node.params.pop().argument; var argsId = t.identifier("arguments"); // otherwise `arguments` will be remapped in arrow functions argsId._shadowedFunctionLiteral = true; // support patterns if (t.isPattern(rest)) { var pattern = rest; rest = scope.generateUidIdentifier("ref"); var declar = t.variableDeclaration("let", pattern.elements.map(function (elem, index) { var accessExpr = t.memberExpression(rest, t.literal(index), true); return t.variableDeclarator(elem, accessExpr); })); file.checkNode(declar); node.body.body.unshift(declar); } // check if rest is used in member expressions and optimise for those cases var state = { outerBinding: scope.getBindingIdentifier(rest.name), canOptimise: true, candidates: [], method: node, name: rest.name }; this.traverse(memberExpressionOptimisationVisitor, state); // we only have shorthands and there's no other references if (state.canOptimise && state.candidates.length) { for (var i = 0; i < state.candidates.length; i++) { var candidate = state.candidates[i]; candidate.replaceWith(argsId); optimizeMemberExpression(candidate.parent, node.params.length); } return; } // var start = t.literal(node.params.length); var key = scope.generateUidIdentifier("key"); var len = scope.generateUidIdentifier("len"); var arrKey = key; var arrLen = len; if (node.params.length) { // this method has additional params, so we need to subtract // the index of the current argument position from the // position in the array that we want to populate arrKey = t.binaryExpression("-", key, start); // we need to work out the size of the array that we're // going to store all the rest parameters // // we need to add a check to avoid constructing the array // with <0 if there are less arguments than params as it'll // cause an error arrLen = t.conditionalExpression(t.binaryExpression(">", len, start), t.binaryExpression("-", len, start), t.literal(0)); } var loop = util.template("rest", { ARGUMENTS: argsId, ARRAY_KEY: arrKey, ARRAY_LEN: arrLen, START: start, ARRAY: rest, KEY: key, LEN: len }); loop._blockHoist = node.params.length + 1; node.body.body.unshift(loop); };