| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 | /** * Copyright (c) 2014-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */"use strict";var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");var _assert = _interopRequireDefault(require("assert"));var _hoist = require("./hoist");var _emit = require("./emit");var _replaceShorthandObjectMethod = _interopRequireDefault(require("./replaceShorthandObjectMethod"));var util = _interopRequireWildcard(require("./util"));exports.getVisitor = function (_ref) {  var t = _ref.types;  return {    Method: function Method(path, state) {      var node = path.node;      if (!shouldRegenerate(node, state)) return;      var container = t.functionExpression(null, [], t.cloneNode(node.body, false), node.generator, node.async);      path.get("body").set("body", [t.returnStatement(t.callExpression(container, []))]); // Regardless of whether or not the wrapped function is a an async method      // or generator the outer function should not be      node.async = false;      node.generator = false; // Unwrap the wrapper IIFE's environment so super and this and such still work.      path.get("body.body.0.argument.callee").unwrapFunctionEnvironment();    },    Function: {      exit: util.wrapWithTypes(t, function (path, state) {        var node = path.node;        if (!shouldRegenerate(node, state)) return; // if this is an ObjectMethod, we need to convert it to an ObjectProperty        path = (0, _replaceShorthandObjectMethod["default"])(path);        node = path.node;        var contextId = path.scope.generateUidIdentifier("context");        var argsId = path.scope.generateUidIdentifier("args");        path.ensureBlock();        var bodyBlockPath = path.get("body");        if (node.async) {          bodyBlockPath.traverse(awaitVisitor);        }        bodyBlockPath.traverse(functionSentVisitor, {          context: contextId        });        var outerBody = [];        var innerBody = [];        bodyBlockPath.get("body").forEach(function (childPath) {          var node = childPath.node;          if (t.isExpressionStatement(node) && t.isStringLiteral(node.expression)) {            // Babylon represents directives like "use strict" as elements            // of a bodyBlockPath.node.directives array, but they could just            // as easily be represented (by other parsers) as traditional            // string-literal-valued expression statements, so we need to            // handle that here. (#248)            outerBody.push(node);          } else if (node && node._blockHoist != null) {            outerBody.push(node);          } else {            innerBody.push(node);          }        });        if (outerBody.length > 0) {          // Only replace the inner body if we actually hoisted any statements          // to the outer body.          bodyBlockPath.node.body = innerBody;        }        var outerFnExpr = getOuterFnExpr(path); // Note that getOuterFnExpr has the side-effect of ensuring that the        // function has a name (so node.id will always be an Identifier), even        // if a temporary name has to be synthesized.        t.assertIdentifier(node.id);        var innerFnId = t.identifier(node.id.name + "$"); // Turn all declarations into vars, and replace the original        // declarations with equivalent assignment expressions.        var vars = (0, _hoist.hoist)(path);        var context = {          usesThis: false,          usesArguments: false,          getArgsId: function getArgsId() {            return t.clone(argsId);          }        };        path.traverse(argumentsThisVisitor, context);        if (context.usesArguments) {          vars = vars || t.variableDeclaration("var", []);          vars.declarations.push(t.variableDeclarator(t.clone(argsId), t.identifier("arguments")));        }        var emitter = new _emit.Emitter(contextId);        emitter.explode(path.get("body"));        if (vars && vars.declarations.length > 0) {          outerBody.push(vars);        }        var wrapArgs = [emitter.getContextFunction(innerFnId)];        var tryLocsList = emitter.getTryLocsList();        if (node.generator) {          wrapArgs.push(outerFnExpr);        } else if (context.usesThis || tryLocsList || node.async) {          // Async functions that are not generators don't care about the          // outer function because they don't need it to be marked and don't          // inherit from its .prototype.          wrapArgs.push(t.nullLiteral());        }        if (context.usesThis) {          wrapArgs.push(t.thisExpression());        } else if (tryLocsList || node.async) {          wrapArgs.push(t.nullLiteral());        }        if (tryLocsList) {          wrapArgs.push(tryLocsList);        } else if (node.async) {          wrapArgs.push(t.nullLiteral());        }        if (node.async) {          // Rename any locally declared "Promise" variable,          // to use the global one.          var currentScope = path.scope;          do {            if (currentScope.hasOwnBinding("Promise")) currentScope.rename("Promise");          } while (currentScope = currentScope.parent);          wrapArgs.push(t.identifier("Promise"));        }        var wrapCall = t.callExpression(util.runtimeProperty(node.async ? "async" : "wrap"), wrapArgs);        outerBody.push(t.returnStatement(wrapCall));        node.body = t.blockStatement(outerBody); // We injected a few new variable declarations (for every hoisted var),        // so we need to add them to the scope.        path.get("body.body").forEach(function (p) {          return p.scope.registerDeclaration(p);        });        var oldDirectives = bodyBlockPath.node.directives;        if (oldDirectives) {          // Babylon represents directives like "use strict" as elements of          // a bodyBlockPath.node.directives array. (#248)          node.body.directives = oldDirectives;        }        var wasGeneratorFunction = node.generator;        if (wasGeneratorFunction) {          node.generator = false;        }        if (node.async) {          node.async = false;        }        if (wasGeneratorFunction && t.isExpression(node)) {          util.replaceWithOrRemove(path, t.callExpression(util.runtimeProperty("mark"), [node]));          path.addComment("leading", "#__PURE__");        }        var insertedLocs = emitter.getInsertedLocs();        path.traverse({          NumericLiteral: function NumericLiteral(path) {            if (!insertedLocs.has(path.node)) {              return;            }            path.replaceWith(t.numericLiteral(path.node.value));          }        }); // Generators are processed in 'exit' handlers so that regenerator only has to run on        // an ES5 AST, but that means traversal will not pick up newly inserted references        // to things like 'regeneratorRuntime'. To avoid this, we explicitly requeue.        path.requeue();      })    }  };}; // Check if a node should be transformed by regeneratorfunction shouldRegenerate(node, state) {  if (node.generator) {    if (node.async) {      // Async generator      return state.opts.asyncGenerators !== false;    } else {      // Plain generator      return state.opts.generators !== false;    }  } else if (node.async) {    // Async function    return state.opts.async !== false;  } else {    // Not a generator or async function.    return false;  }} // Given a NodePath for a Function, return an Expression node that can be// used to refer reliably to the function object from inside the function.// This expression is essentially a replacement for arguments.callee, with// the key advantage that it works in strict mode.function getOuterFnExpr(funPath) {  var t = util.getTypes();  var node = funPath.node;  t.assertFunction(node);  if (!node.id) {    // Default-exported function declarations, and function expressions may not    // have a name to reference, so we explicitly add one.    node.id = funPath.scope.parent.generateUidIdentifier("callee");  }  if (node.generator && // Non-generator functions don't need to be marked.  t.isFunctionDeclaration(node)) {    // Return the identifier returned by runtime.mark(<node.id>).    return getMarkedFunctionId(funPath);  }  return t.clone(node.id);}var markInfo = new WeakMap();function getMarkInfo(node) {  if (!markInfo.has(node)) {    markInfo.set(node, {});  }  return markInfo.get(node);}function getMarkedFunctionId(funPath) {  var t = util.getTypes();  var node = funPath.node;  t.assertIdentifier(node.id);  var blockPath = funPath.findParent(function (path) {    return path.isProgram() || path.isBlockStatement();  });  if (!blockPath) {    return node.id;  }  var block = blockPath.node;  _assert["default"].ok(Array.isArray(block.body));  var info = getMarkInfo(block);  if (!info.decl) {    info.decl = t.variableDeclaration("var", []);    blockPath.unshiftContainer("body", info.decl);    info.declPath = blockPath.get("body.0");  }  _assert["default"].strictEqual(info.declPath.node, info.decl); // Get a new unique identifier for our marked variable.  var markedId = blockPath.scope.generateUidIdentifier("marked");  var markCallExp = t.callExpression(util.runtimeProperty("mark"), [t.clone(node.id)]);  var index = info.decl.declarations.push(t.variableDeclarator(markedId, markCallExp)) - 1;  var markCallExpPath = info.declPath.get("declarations." + index + ".init");  _assert["default"].strictEqual(markCallExpPath.node, markCallExp);  markCallExpPath.addComment("leading", "#__PURE__");  return t.clone(markedId);}var argumentsThisVisitor = {  "FunctionExpression|FunctionDeclaration|Method": function FunctionExpressionFunctionDeclarationMethod(path) {    path.skip();  },  Identifier: function Identifier(path, state) {    if (path.node.name === "arguments" && util.isReference(path)) {      util.replaceWithOrRemove(path, state.getArgsId());      state.usesArguments = true;    }  },  ThisExpression: function ThisExpression(path, state) {    state.usesThis = true;  }};var functionSentVisitor = {  MetaProperty: function MetaProperty(path) {    var node = path.node;    if (node.meta.name === "function" && node.property.name === "sent") {      var t = util.getTypes();      util.replaceWithOrRemove(path, t.memberExpression(t.clone(this.context), t.identifier("_sent")));    }  }};var awaitVisitor = {  Function: function Function(path) {    path.skip(); // Don't descend into nested function scopes.  },  AwaitExpression: function AwaitExpression(path) {    var t = util.getTypes(); // Convert await expressions to yield expressions.    var argument = path.node.argument; // Transforming `await x` to `yield regeneratorRuntime.awrap(x)`    // causes the argument to be wrapped in such a way that the runtime    // can distinguish between awaited and merely yielded values.    util.replaceWithOrRemove(path, t.yieldExpression(t.callExpression(util.runtimeProperty("awrap"), [argument]), false));  }};
 |