123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.default = convertFunctionRest;
- var _core = require("@babel/core");
- var _shadowUtils = require("./shadow-utils");
- const buildRest = _core.template.statement(`
- for (var LEN = ARGUMENTS.length,
- ARRAY = new Array(ARRAY_LEN),
- KEY = START;
- KEY < LEN;
- KEY++) {
- ARRAY[ARRAY_KEY] = ARGUMENTS[KEY];
- }
- `);
- const restIndex = _core.template.expression(`
- (INDEX < OFFSET || ARGUMENTS.length <= INDEX) ? undefined : ARGUMENTS[INDEX]
- `);
- const restIndexImpure = _core.template.expression(`
- REF = INDEX, (REF < OFFSET || ARGUMENTS.length <= REF) ? undefined : ARGUMENTS[REF]
- `);
- const restLength = _core.template.expression(`
- ARGUMENTS.length <= OFFSET ? 0 : ARGUMENTS.length - OFFSET
- `);
- function referencesRest(path, state) {
- if (path.node.name === state.name) {
- return path.scope.bindingIdentifierEquals(state.name, state.outerBinding);
- }
- return false;
- }
- const memberExpressionOptimisationVisitor = {
- Scope(path, state) {
- if (!path.scope.bindingIdentifierEquals(state.name, state.outerBinding)) {
- path.skip();
- }
- },
- Flow(path) {
- if (path.isTypeCastExpression()) return;
- path.skip();
- },
- Function(path, state) {
- const oldNoOptimise = state.noOptimise;
- state.noOptimise = true;
- path.traverse(memberExpressionOptimisationVisitor, state);
- state.noOptimise = oldNoOptimise;
- path.skip();
- },
- ReferencedIdentifier(path, state) {
- const {
- node
- } = path;
- if (node.name === "arguments") {
- state.deopted = true;
- }
- if (!referencesRest(path, state)) return;
- if (state.noOptimise) {
- state.deopted = true;
- } else {
- const {
- parentPath
- } = path;
- if (parentPath.listKey === "params" && parentPath.key < state.offset) {
- return;
- }
- if (parentPath.isMemberExpression({
- object: node
- })) {
- const grandparentPath = parentPath.parentPath;
- const argsOptEligible = !state.deopted && !(
- grandparentPath.isAssignmentExpression() && parentPath.node === grandparentPath.node.left ||
- grandparentPath.isLVal() ||
- grandparentPath.isForXStatement() ||
- grandparentPath.isUpdateExpression() ||
- grandparentPath.isUnaryExpression({
- operator: "delete"
- }) ||
- (grandparentPath.isCallExpression() || grandparentPath.isNewExpression()) && parentPath.node === grandparentPath.node.callee);
- if (argsOptEligible) {
- if (parentPath.node.computed) {
- if (parentPath.get("property").isBaseType("number")) {
- state.candidates.push({
- cause: "indexGetter",
- path
- });
- return;
- }
- } else if (
- parentPath.node.property.name === "length") {
- state.candidates.push({
- cause: "lengthGetter",
- path
- });
- return;
- }
- }
- }
- if (state.offset === 0 && parentPath.isSpreadElement()) {
- const call = parentPath.parentPath;
- if (call.isCallExpression() && call.node.arguments.length === 1) {
- state.candidates.push({
- cause: "argSpread",
- path
- });
- return;
- }
- }
- state.references.push(path);
- }
- },
- BindingIdentifier(path, state) {
- if (referencesRest(path, state)) {
- state.deopted = true;
- }
- }
- };
- function getParamsCount(node) {
- let count = node.params.length;
- if (count > 0 && _core.types.isIdentifier(node.params[0], {
- name: "this"
- })) {
- count -= 1;
- }
- return count;
- }
- function hasRest(node) {
- const length = node.params.length;
- return length > 0 && _core.types.isRestElement(node.params[length - 1]);
- }
- function optimiseIndexGetter(path, argsId, offset) {
- const offsetLiteral = _core.types.numericLiteral(offset);
- let index;
- const parent = path.parent;
- if (_core.types.isNumericLiteral(parent.property)) {
- index = _core.types.numericLiteral(parent.property.value + offset);
- } else if (offset === 0) {
- index = parent.property;
- } else {
- index = _core.types.binaryExpression("+", parent.property, _core.types.cloneNode(offsetLiteral));
- }
- const {
- scope,
- parentPath
- } = path;
- if (!scope.isPure(index)) {
- const temp = scope.generateUidIdentifierBasedOnNode(index);
- scope.push({
- id: temp,
- kind: "var"
- });
- parentPath.replaceWith(restIndexImpure({
- ARGUMENTS: argsId,
- OFFSET: offsetLiteral,
- INDEX: index,
- REF: _core.types.cloneNode(temp)
- }));
- } else {
- parentPath.replaceWith(restIndex({
- ARGUMENTS: argsId,
- OFFSET: offsetLiteral,
- INDEX: index
- }));
- const replacedParentPath = parentPath;
- const offsetTestPath = replacedParentPath.get("test");
- const valRes = offsetTestPath.get("left").evaluate();
- if (valRes.confident) {
- if (valRes.value === true) {
- replacedParentPath.replaceWith(scope.buildUndefinedNode());
- } else {
- offsetTestPath.replaceWith(offsetTestPath.get("right"));
- }
- }
- }
- }
- function optimiseLengthGetter(path, argsId, offset) {
- if (offset) {
- path.parentPath.replaceWith(restLength({
- ARGUMENTS: argsId,
- OFFSET: _core.types.numericLiteral(offset)
- }));
- } else {
- path.replaceWith(argsId);
- }
- }
- function convertFunctionRest(path) {
- const {
- node,
- scope
- } = path;
- if (!hasRest(node)) return false;
- const restPath = path.get(`params.${node.params.length - 1}.argument`);
- if (!restPath.isIdentifier()) {
- const shadowedParams = new Set();
- (0, _shadowUtils.collectShadowedParamsNames)(restPath, path.scope, shadowedParams);
- let needsIIFE = shadowedParams.size > 0;
- if (!needsIIFE) {
- const state = {
- needsOuterBinding: false,
- scope
- };
- restPath.traverse(_shadowUtils.iifeVisitor, state);
- needsIIFE = state.needsOuterBinding;
- }
- if (needsIIFE) {
- path.ensureBlock();
- path.set("body", _core.types.blockStatement([(0, _shadowUtils.buildScopeIIFE)(shadowedParams, path.node.body)]));
- }
- }
- let rest = restPath.node;
- node.params.pop();
- if (_core.types.isPattern(rest)) {
- const pattern = rest;
- rest = scope.generateUidIdentifier("ref");
- const declar = _core.types.variableDeclaration("let", [_core.types.variableDeclarator(pattern, rest)]);
- path.ensureBlock();
- node.body.body.unshift(declar);
- } else if (rest.name === "arguments") {
- scope.rename(rest.name);
- }
- const argsId = _core.types.identifier("arguments");
- const paramsCount = getParamsCount(node);
- const state = {
- references: [],
- offset: paramsCount,
- argumentsNode: argsId,
- outerBinding: scope.getBindingIdentifier(rest.name),
- candidates: [],
- name: rest.name,
- deopted: false
- };
- path.traverse(memberExpressionOptimisationVisitor, state);
- if (!state.deopted && !state.references.length) {
- for (const {
- path,
- cause
- } of state.candidates) {
- const clonedArgsId = _core.types.cloneNode(argsId);
- switch (cause) {
- case "indexGetter":
- optimiseIndexGetter(path, clonedArgsId, state.offset);
- break;
- case "lengthGetter":
- optimiseLengthGetter(path, clonedArgsId, state.offset);
- break;
- default:
- path.replaceWith(clonedArgsId);
- }
- }
- return true;
- }
- state.references.push(...state.candidates.map(({
- path
- }) => path));
- const start = _core.types.numericLiteral(paramsCount);
- const key = scope.generateUidIdentifier("key");
- const len = scope.generateUidIdentifier("len");
- let arrKey, arrLen;
- if (paramsCount) {
- arrKey = _core.types.binaryExpression("-", _core.types.cloneNode(key), _core.types.cloneNode(start));
- arrLen = _core.types.conditionalExpression(_core.types.binaryExpression(">", _core.types.cloneNode(len), _core.types.cloneNode(start)), _core.types.binaryExpression("-", _core.types.cloneNode(len), _core.types.cloneNode(start)), _core.types.numericLiteral(0));
- } else {
- arrKey = _core.types.identifier(key.name);
- arrLen = _core.types.identifier(len.name);
- }
- const loop = buildRest({
- ARGUMENTS: argsId,
- ARRAY_KEY: arrKey,
- ARRAY_LEN: arrLen,
- START: start,
- ARRAY: rest,
- KEY: key,
- LEN: len
- });
- if (state.deopted) {
- node.body.body.unshift(loop);
- } else {
- let target = path.getEarliestCommonAncestorFrom(state.references).getStatementParent();
- target.findParent(path => {
- if (path.isLoop()) {
- target = path;
- } else {
- return path.isFunction();
- }
- });
- target.insertBefore(loop);
- }
- return true;
- }
- //# sourceMappingURL=rest.js.map
|