UNPKG

@babel/plugin-transform-regenerator

Version:

Explode async and generator functions into a state machine.

1,120 lines (1,110 loc) 39.5 kB
import { declare } from '@babel/helper-plugin-utils'; import assert from 'node:assert'; import { types, traverse } from '@babel/core'; function hoist(funPath) { types.assertFunction(funPath.node); const vars = { __proto__: null }; function varDeclToExpr({ node: vdec }, includeIdentifiers) { types.assertVariableDeclaration(vdec); const exprs = []; vdec.declarations.forEach(function (dec) { vars[dec.id.name] = types.identifier(dec.id.name); if (dec.init) { exprs.push(types.assignmentExpression("=", dec.id, dec.init)); } else if (includeIdentifiers) { exprs.push(dec.id); } }); if (exprs.length === 0) return null; if (exprs.length === 1) return exprs[0]; return types.sequenceExpression(exprs); } funPath.get("body").traverse({ VariableDeclaration: { exit: function (path) { const expr = varDeclToExpr(path, false); if (expr === null) { path.remove(); } else { for (const name of Object.keys(vars)) { path.scope.removeBinding(name); } path.replaceWith(types.expressionStatement(expr)); } path.skip(); } }, ForStatement: function (path) { const init = path.get("init"); if (init.isVariableDeclaration()) { const expr = varDeclToExpr(init, false); if (expr) { init.replaceWith(expr); } else { init.remove(); } } }, ForXStatement: function (path) { const left = path.get("left"); if (left.isVariableDeclaration()) { left.replaceWith(varDeclToExpr(left, true)); } }, FunctionDeclaration: function (path) { const node = path.node; vars[node.id.name] = node.id; const assignment = types.expressionStatement(types.assignmentExpression("=", types.cloneNode(node.id), types.functionExpression(path.scope.generateUidIdentifierBasedOnNode(node), node.params, node.body, node.generator, node.async))); if (path.parentPath.isBlockStatement()) { path.parentPath.unshiftContainer("body", assignment); path.remove(); } else { path.replaceWith(assignment); path.scope.removeBinding(node.id.name); } path.skip(); }, FunctionExpression: function (path) { path.skip(); }, ArrowFunctionExpression: function (path) { path.skip(); } }); const paramNames = { __proto__: null }; funPath.get("params").forEach(function (paramPath) { const param = paramPath.node; if (types.isIdentifier(param)) { paramNames[param.name] = param; } }); const declarations = []; Object.keys(vars).forEach(function (name) { if (!Object.hasOwn(paramNames, name)) { declarations.push(types.variableDeclarator(vars[name], null)); } }); return declarations; } class Entry {} class FunctionEntry extends Entry { returnLoc; constructor(returnLoc) { super(); this.returnLoc = returnLoc; } } class LoopEntry extends Entry { breakLoc; continueLoc; label; constructor(breakLoc, continueLoc, label = null) { super(); this.breakLoc = breakLoc; this.continueLoc = continueLoc; this.label = label; } } class SwitchEntry extends Entry { breakLoc; constructor(breakLoc) { super(); this.breakLoc = breakLoc; } } class TryEntry extends Entry { firstLoc; catchEntry; finallyEntry; constructor(firstLoc, catchEntry = null, finallyEntry = null) { super(); assert.ok(catchEntry || finallyEntry); this.firstLoc = firstLoc; this.catchEntry = catchEntry; this.finallyEntry = finallyEntry; } } class CatchEntry extends Entry { firstLoc; paramId; constructor(firstLoc, paramId) { super(); this.firstLoc = firstLoc; this.paramId = paramId; } } class FinallyEntry extends Entry { firstLoc; afterLoc; constructor(firstLoc, afterLoc) { super(); this.firstLoc = firstLoc; this.afterLoc = afterLoc; } } class LabeledEntry extends Entry { breakLoc; label; constructor(breakLoc, label) { super(); this.breakLoc = breakLoc; this.label = label; } } class LeapManager { emitter; entryStack; constructor(emitter) { this.emitter = emitter; this.entryStack = [new FunctionEntry(emitter.finalLoc)]; } withEntry(entry, callback) { this.entryStack.push(entry); try { callback.call(this.emitter); } finally { const popped = this.entryStack.pop(); assert.strictEqual(popped, entry); } } _findLeapLocation(property, label) { for (let i = this.entryStack.length - 1; i >= 0; --i) { const entry = this.entryStack[i]; const loc = entry[property]; if (loc) { if (label) { if (entry.label && entry.label.name === label.name) { return loc; } } else if (entry instanceof LabeledEntry) ; else { return loc; } } } throw new Error("unreachable"); } getBreakLoc(label) { return this._findLeapLocation("breakLoc", label); } getContinueLoc(label) { return this._findLeapLocation("continueLoc", label); } } const mMap = new WeakMap(); function m(node) { if (!mMap.has(node)) { mMap.set(node, {}); } return mMap.get(node); } function makePredicate(propertyName, knownTypes) { function onlyChildren(node) { types.assertNode(node); let result = false; function check(child) { if (result) ; else if (Array.isArray(child)) { child.some(check); } else if (types.isNode(child)) { assert.strictEqual(result, false); result = predicate(child); } return result; } const keys = types.VISITOR_KEYS[node.type]; if (keys) { for (let i = 0; i < keys.length; i++) { const key = keys[i]; const child = node[key]; check(child); } } return result; } function predicate(node) { types.assertNode(node); const meta = m(node); if (Object.hasOwn(meta, propertyName)) return meta[propertyName]; if (Object.hasOwn(opaqueTypes, node.type)) return meta[propertyName] = false; if (Object.hasOwn(knownTypes, node.type)) return meta[propertyName] = true; return meta[propertyName] = onlyChildren(node); } predicate.onlyChildren = onlyChildren; return predicate; } const opaqueTypes = { FunctionExpression: true, ArrowFunctionExpression: true }; const sideEffectTypes = { CallExpression: true, ForInStatement: true, UnaryExpression: true, BinaryExpression: true, AssignmentExpression: true, UpdateExpression: true, NewExpression: true }; const leapTypes = { YieldExpression: true, AwaitExpression: true, BreakStatement: true, ContinueStatement: true, ReturnStatement: true, ThrowStatement: true }; for (const type in leapTypes) { if (Object.hasOwn(leapTypes, type)) { sideEffectTypes[type] = leapTypes[type]; } } makePredicate("hasSideEffects", sideEffectTypes); const containsLeap = makePredicate("containsLeap", leapTypes); function isReference(path) { return path.isReferenced() || path.parentPath.isAssignmentExpression({ left: path.node }); } const PENDING_LOCATION = Number.MAX_VALUE; function getDeclError(node) { return new Error("all declarations should have been transformed into " + "assignments before the Exploder began its work: " + JSON.stringify(node)); } const catchParamVisitor = { Identifier: function (path, state) { if (path.node.name === state.catchParamName && isReference(path)) { path.replaceWith(state.getSafeParam()); } }, Scope: function (path, state) { if (path.scope.hasOwnBinding(state.catchParamName)) { path.skip(); } } }; class Emitter { nextTempId; contextId; index; indexMap; listing; returns; lastReferenceIndex = 0; marked; insertedLocs; finalLoc; tryEntries; leapManager; scope; vars; pluginPass; constructor(contextId, scope, vars, pluginPass) { this.pluginPass = pluginPass; this.scope = scope; this.vars = vars; this.nextTempId = 0; this.contextId = contextId; this.listing = []; this.index = 0; this.indexMap = new Map([[0, 0]]); this.returns = new Set(); this.lastReferenceIndex = 0; this.marked = [true]; this.insertedLocs = new Set(); this.finalLoc = this.loc(); this.tryEntries = []; this.leapManager = new LeapManager(this); } loc() { const l = types.numericLiteral(PENDING_LOCATION); this.insertedLocs.add(l); return l; } getInsertedLocs() { return this.insertedLocs; } getContextId() { return types.cloneNode(this.contextId); } getIndex() { if (!this.indexMap.has(this.listing.length)) { this.indexMap.set(this.listing.length, ++this.index); } return this.index; } mark(loc) { if (loc.value === PENDING_LOCATION) { loc.value = this.getIndex(); } else { assert.strictEqual(loc.value, this.index); } this.marked[this.listing.length] = true; if (loc.value > this.lastReferenceIndex) { this.lastReferenceIndex = loc.value; } return loc; } emit(node) { if (types.isExpression(node)) { node = types.expressionStatement(node); } types.assertStatement(node); this.listing.push(node); } emitAssign(lhs, rhs) { this.emit(this.assign(lhs, rhs)); return lhs; } assign(lhs, rhs) { return types.expressionStatement(types.assignmentExpression("=", types.cloneNode(lhs), rhs)); } contextProperty(name) { const computed = name === "catch"; return types.memberExpression(this.getContextId(), computed ? types.stringLiteral(name) : types.identifier(name), !!computed); } clearPendingException(tryLoc, assignee) { const catchCall = types.callExpression(this.contextProperty("catch"), [types.cloneNode(tryLoc)]); if (assignee) { this.emitAssign(assignee, catchCall); } else { this.emit(catchCall); } } jump(toLoc) { this.emitAssign(this.contextProperty("n"), toLoc); this.emit(types.breakStatement()); } jumpIf(test, toLoc) { this.emit(types.ifStatement(test, types.blockStatement([this.assign(this.contextProperty("n"), toLoc), types.breakStatement()]))); } jumpIfNot(test, toLoc) { let negatedTest; if (types.isUnaryExpression(test) && test.operator === "!") { negatedTest = test.argument; } else { negatedTest = types.unaryExpression("!", test); } this.emit(types.ifStatement(negatedTest, types.blockStatement([this.assign(this.contextProperty("n"), toLoc), types.breakStatement()]))); } makeContextTempVar() { return this.contextProperty("t" + this.nextTempId++); } makeTempVar() { const id = this.scope.generateUidIdentifier("t"); this.vars.push(types.variableDeclarator(id)); return types.cloneNode(id); } getContextFunction() { return types.functionExpression(null, [this.getContextId()], types.blockStatement([this.getDispatchLoop()]), false, false); } getDispatchLoop() { const self = this; const cases = []; let current; let alreadyEnded = false; self.listing.forEach(function (stmt, i) { if (self.marked[i]) { cases.push(types.switchCase(types.numericLiteral(self.indexMap.get(i)), current = [])); alreadyEnded = false; } if (!alreadyEnded) { current.push(stmt); if (types.isCompletionStatement(stmt)) alreadyEnded = true; } }); this.finalLoc.value = this.getIndex(); if (this.lastReferenceIndex === this.index || !this.returns.has(this.listing.length)) { cases.push(types.switchCase(this.finalLoc, [types.returnStatement(types.callExpression(this.contextProperty("a"), [types.numericLiteral(2)]))])); } return types.whileStatement(types.numericLiteral(1), types.switchStatement(this.tryEntries.length === 0 ? this.contextProperty("n") : types.assignmentExpression("=", this.contextProperty("p"), this.contextProperty("n")), cases)); } getTryLocsList() { if (this.tryEntries.length === 0) { return null; } let lastLocValue = 0; const arrayExpression = types.arrayExpression(this.tryEntries.map(function (tryEntry) { const thisLocValue = tryEntry.firstLoc.value; assert.ok(thisLocValue >= lastLocValue, "try entries out of order"); lastLocValue = thisLocValue; const ce = tryEntry.catchEntry; const fe = tryEntry.finallyEntry; const locs = [tryEntry.firstLoc, ce ? ce.firstLoc : null]; if (fe) { locs[2] = fe.firstLoc; locs[3] = fe.afterLoc; } return types.arrayExpression(locs.map(loc => loc && types.cloneNode(loc))); })); arrayExpression.elements.reverse(); return arrayExpression; } explode(path, ignoreResult) { const node = path.node; const self = this; if (types.isDeclaration(node)) throw getDeclError(node); if (path.isStatement()) return self.explodeStatement(path); if (path.isExpression()) return self.explodeExpression(path, ignoreResult); switch (node.type) { case "VariableDeclarator": throw getDeclError(node); case "ObjectProperty": case "SwitchCase": case "CatchClause": throw new Error(node.type + " nodes should be handled by their parents"); default: throw new Error("unknown Node of type " + JSON.stringify(node.type)); } } explodeStatement(path, labelId = null) { const stmt = path.node; const self = this; let before, after, head; if (path.isBlockStatement()) { path.get("body").forEach(function (path) { self.explodeStatement(path); }); return; } if (!containsLeap(stmt)) { self.emit(stmt); return; } switch (path.type) { case "ExpressionStatement": self.explodeExpression(path.get("expression"), true); break; case "LabeledStatement": after = this.loc(); self.leapManager.withEntry(new LabeledEntry(after, path.node.label), function () { self.explodeStatement(path.get("body"), path.node.label); }); self.mark(after); break; case "WhileStatement": before = this.loc(); after = this.loc(); self.mark(before); self.jumpIfNot(self.explodeExpression(path.get("test")), after); self.leapManager.withEntry(new LoopEntry(after, before, labelId), function () { self.explodeStatement(path.get("body")); }); self.jump(before); self.mark(after); break; case "DoWhileStatement": const first = this.loc(); const test = this.loc(); after = this.loc(); self.mark(first); self.leapManager.withEntry(new LoopEntry(after, test, labelId), function () { self.explode(path.get("body")); }); self.mark(test); self.jumpIf(self.explodeExpression(path.get("test")), first); self.mark(after); break; case "ForStatement": head = this.loc(); const update = this.loc(); after = this.loc(); if (path.node.init) { self.explode(path.get("init"), true); } self.mark(head); if (path.node.test) { self.jumpIfNot(self.explodeExpression(path.get("test")), after); } self.leapManager.withEntry(new LoopEntry(after, update, labelId), function () { self.explodeStatement(path.get("body")); }); self.mark(update); if (path.node.update) { self.explode(path.get("update"), true); } self.jump(head); self.mark(after); break; case "TypeCastExpression": return self.explodeExpression(path.get("expression")); case "ForInStatement": head = this.loc(); after = this.loc(); const keyIterNextFn = self.makeTempVar(); const helper = this.pluginPass.addHelper("regeneratorKeys"); self.emitAssign(keyIterNextFn, types.callExpression(helper, [self.explodeExpression(path.get("right"))])); self.mark(head); const keyInfoTmpVar = self.makeTempVar(); self.jumpIf(types.memberExpression(types.assignmentExpression("=", keyInfoTmpVar, types.callExpression(types.cloneNode(keyIterNextFn), [])), types.identifier("done"), false), after); self.emitAssign(path.node.left, types.memberExpression(types.cloneNode(keyInfoTmpVar), types.identifier("value"), false)); self.leapManager.withEntry(new LoopEntry(after, head, labelId), function () { self.explodeStatement(path.get("body")); }); self.jump(head); self.mark(after); break; case "BreakStatement": self.emitAbruptCompletion({ type: 3, target: self.leapManager.getBreakLoc(path.node.label) }); break; case "ContinueStatement": self.emitAbruptCompletion({ type: 3, target: self.leapManager.getContinueLoc(path.node.label) }); break; case "SwitchStatement": const disc = self.emitAssign(self.makeTempVar(), self.explodeExpression(path.get("discriminant"))); after = this.loc(); const defaultLoc = this.loc(); let condition = defaultLoc; const caseLocs = []; const cases = path.node.cases || []; for (let i = cases.length - 1; i >= 0; --i) { const c = cases[i]; if (c.test) { condition = types.conditionalExpression(types.binaryExpression("===", types.cloneNode(disc), c.test), caseLocs[i] = this.loc(), condition); } else { caseLocs[i] = defaultLoc; } } const discriminant = path.get("discriminant"); discriminant.replaceWith(condition); self.jump(self.explodeExpression(discriminant)); self.leapManager.withEntry(new SwitchEntry(after), function () { path.get("cases").forEach(function (casePath) { const i = casePath.key; self.mark(caseLocs[i]); casePath.get("consequent").forEach(function (path) { self.explodeStatement(path); }); }); }); self.mark(after); if (defaultLoc.value === PENDING_LOCATION) { self.mark(defaultLoc); assert.strictEqual(after.value, defaultLoc.value); } break; case "IfStatement": const elseLoc = path.node.alternate && this.loc(); after = this.loc(); self.jumpIfNot(self.explodeExpression(path.get("test")), elseLoc || after); self.explodeStatement(path.get("consequent")); if (elseLoc) { self.jump(after); self.mark(elseLoc); self.explodeStatement(path.get("alternate")); } self.mark(after); break; case "ReturnStatement": self.emitAbruptCompletion({ type: 2, value: self.explodeExpression(path.get("argument")) }); break; case "WithStatement": throw new Error("WithStatement not supported in generator functions."); case "TryStatement": after = this.loc(); const handler = path.node.handler; const catchLoc = handler && this.loc(); const catchEntry = catchLoc && new CatchEntry(catchLoc, handler.param); const finallyLoc = path.node.finalizer && this.loc(); const finallyEntry = finallyLoc && new FinallyEntry(finallyLoc, after); const tryEntry = new TryEntry(self.getUnmarkedCurrentLoc(), catchEntry, finallyEntry); self.tryEntries.push(tryEntry); self.updateContextPrevLoc(tryEntry.firstLoc); self.leapManager.withEntry(tryEntry, () => { self.explodeStatement(path.get("block")); if (catchLoc) { const body = path.node.block.body; if (finallyLoc) { self.jump(finallyLoc); } else if (body.length && body[body.length - 1].type === "ReturnStatement") { after = null; } else { self.jump(after); } self.updateContextPrevLoc(self.mark(catchLoc)); const bodyPath = path.get("handler.body"); const safeParam = self.makeTempVar(); this.emitAssign(safeParam, self.contextProperty("v")); bodyPath.traverse(catchParamVisitor, { getSafeParam: () => types.cloneNode(safeParam), catchParamName: handler.param.name }); self.leapManager.withEntry(catchEntry, function () { self.explodeStatement(bodyPath); }); } if (finallyLoc) { self.updateContextPrevLoc(self.mark(finallyLoc)); self.leapManager.withEntry(finallyEntry, function () { self.explodeStatement(path.get("finalizer")); }); self.emit(types.returnStatement(types.callExpression(self.contextProperty("f"), [finallyEntry.firstLoc]))); } }); if (after) self.mark(after); break; case "ThrowStatement": self.emit(types.throwStatement(self.explodeExpression(path.get("argument")))); break; case "ClassDeclaration": self.emit(self.explodeClass(path)); break; default: throw new Error("unknown Statement of type " + JSON.stringify(stmt.type)); } } emitAbruptCompletion(record) { const abruptArgs = [types.numericLiteral(record.type)]; if (record.type === 3) { abruptArgs[1] = this.insertedLocs.has(record.target) ? record.target : types.cloneNode(record.target); } else if (record.type === 2) { if (record.value) { abruptArgs[1] = types.cloneNode(record.value); } } this.emit(types.returnStatement(types.callExpression(this.contextProperty("a"), abruptArgs))); if (record.type === 2) { this.returns.add(this.listing.length); } } getUnmarkedCurrentLoc() { return types.numericLiteral(this.getIndex()); } updateContextPrevLoc(loc) { if (loc) { if (loc.value === PENDING_LOCATION) { loc.value = this.getIndex(); } else { assert.strictEqual(loc.value, this.index); } } else { loc = this.getUnmarkedCurrentLoc(); } this.emitAssign(this.contextProperty("p"), loc); } explodeViaTempVar(tempVar, childPath, hasLeapingChildren, ignoreChildResult) { assert.ok(!ignoreChildResult || !tempVar, "Ignoring the result of a child expression but forcing it to " + "be assigned to a temporary variable?"); let result = this.explodeExpression(childPath, ignoreChildResult); if (ignoreChildResult) ; else if (tempVar || hasLeapingChildren && !types.isLiteral(result)) { result = this.emitAssign(tempVar || this.makeTempVar(), result); } return result; } explodeExpression(path, ignoreResult) { const expr = path.node; if (!expr) { return expr; } const self = this; let result; let after; function finish(expr) { if (ignoreResult) { self.emit(expr); } return expr; } if (!containsLeap(expr)) { return finish(expr); } const hasLeapingChildren = containsLeap.onlyChildren(expr); switch (path.type) { case "MemberExpression": return finish(types.memberExpression(self.explodeExpression(path.get("object")), path.node.computed ? self.explodeViaTempVar(null, path.get("property"), hasLeapingChildren) : path.node.property, path.node.computed)); case "CallExpression": const calleePath = path.get("callee"); const argsPath = path.get("arguments"); let newCallee; let newArgs; let lastLeapingArgIndex = argsPath.length - 1; while (lastLeapingArgIndex >= 0 && !containsLeap(argsPath[lastLeapingArgIndex].node)) { lastLeapingArgIndex--; } let injectFirstArg = null; if (types.isMemberExpression(calleePath.node)) { if (lastLeapingArgIndex !== -1) { const newObject = self.explodeViaTempVar(self.makeTempVar(), calleePath.get("object"), hasLeapingChildren); const newProperty = calleePath.node.computed ? self.explodeViaTempVar(null, calleePath.get("property"), hasLeapingChildren) : calleePath.node.property; injectFirstArg = newObject; newCallee = types.memberExpression(types.memberExpression(types.cloneNode(newObject), newProperty, calleePath.node.computed), types.identifier("call"), false); } else { newCallee = self.explodeExpression(calleePath); } } else { newCallee = self.explodeViaTempVar(null, calleePath, hasLeapingChildren); if (types.isMemberExpression(newCallee)) { newCallee = types.sequenceExpression([types.numericLiteral(0), types.cloneNode(newCallee)]); } } if (lastLeapingArgIndex !== -1) { newArgs = argsPath.map((argPath, index) => index >= lastLeapingArgIndex ? self.explodeExpression(argPath) : self.explodeViaTempVar(null, argPath, hasLeapingChildren)); if (injectFirstArg) newArgs.unshift(injectFirstArg); newArgs = newArgs.map(arg => types.cloneNode(arg)); } else { newArgs = path.node.arguments; } return finish(types.callExpression(newCallee, newArgs)); case "NewExpression": return finish(types.newExpression(self.explodeViaTempVar(null, path.get("callee"), hasLeapingChildren), path.get("arguments").map(function (argPath) { return self.explodeViaTempVar(null, argPath, hasLeapingChildren); }))); case "ObjectExpression": return finish(types.objectExpression(path.get("properties").map(function (propPath) { if (propPath.isObjectProperty()) { return types.objectProperty(propPath.node.key, self.explodeViaTempVar(null, propPath.get("value"), hasLeapingChildren), propPath.node.computed); } else { return propPath.node; } }))); case "ArrayExpression": return finish(types.arrayExpression(path.get("elements").map(function (elemPath) { if (!elemPath.node) { return null; } if (elemPath.isSpreadElement()) { return types.spreadElement(self.explodeViaTempVar(null, elemPath.get("argument"), hasLeapingChildren)); } else { return self.explodeViaTempVar(null, elemPath, hasLeapingChildren); } }))); case "SequenceExpression": const lastIndex = path.node.expressions.length - 1; path.get("expressions").forEach(function (exprPath) { if (exprPath.key === lastIndex) { result = self.explodeExpression(exprPath, ignoreResult); } else { self.explodeExpression(exprPath, true); } }); return result; case "LogicalExpression": after = this.loc(); if (!ignoreResult) { result = self.makeTempVar(); } const left = self.explodeViaTempVar(result, path.get("left"), hasLeapingChildren); if (path.node.operator === "&&") { self.jumpIfNot(left, after); } else { assert.strictEqual(path.node.operator, "||"); self.jumpIf(left, after); } self.explodeViaTempVar(result, path.get("right"), hasLeapingChildren, ignoreResult); self.mark(after); return result; case "ConditionalExpression": const elseLoc = this.loc(); after = this.loc(); const test = self.explodeExpression(path.get("test")); self.jumpIfNot(test, elseLoc); if (!ignoreResult) { result = self.makeTempVar(); } self.explodeViaTempVar(result, path.get("consequent"), hasLeapingChildren, ignoreResult); self.jump(after); self.mark(elseLoc); self.explodeViaTempVar(result, path.get("alternate"), hasLeapingChildren, ignoreResult); self.mark(after); return result; case "UnaryExpression": return finish(types.unaryExpression(path.node.operator, self.explodeExpression(path.get("argument")), !!path.node.prefix)); case "BinaryExpression": return finish(types.binaryExpression(path.node.operator, self.explodeViaTempVar(null, path.get("left"), hasLeapingChildren), self.explodeViaTempVar(null, path.get("right"), hasLeapingChildren))); case "AssignmentExpression": if (path.node.operator === "=") { return finish(types.assignmentExpression(path.node.operator, self.explodeExpression(path.get("left")), self.explodeExpression(path.get("right")))); } const lhs = self.explodeExpression(path.get("left")); const temp = self.emitAssign(self.makeTempVar(), lhs); return finish(types.assignmentExpression("=", types.cloneNode(lhs), types.assignmentExpression(path.node.operator, types.cloneNode(temp), self.explodeExpression(path.get("right"))))); case "UpdateExpression": return finish(types.updateExpression(path.node.operator, self.explodeExpression(path.get("argument")), path.node.prefix)); case "YieldExpression": after = this.loc(); const arg = path.node.argument && self.explodeExpression(path.get("argument")); if (arg && path.node.delegate) { const ret = types.returnStatement(types.callExpression(self.contextProperty("d"), [types.callExpression(this.pluginPass.addHelper("regeneratorValues"), [arg]), after])); ret.loc = expr.loc; self.emit(ret); self.mark(after); return self.contextProperty("v"); } self.emitAssign(self.contextProperty("n"), after); const ret = types.returnStatement(types.cloneNode(arg) || null); ret.loc = expr.loc; self.emit(ret); self.mark(after); return self.contextProperty("v"); case "AwaitExpression": { after = this.loc(); const arg = self.explodeExpression(path.get("argument")); self.emitAssign(self.contextProperty("n"), after); const helper = self.pluginPass.addHelper("awaitAsyncGenerator"); const ret = types.returnStatement(types.cloneNode(types.callExpression(helper, [arg])) || null); ret.loc = expr.loc; self.emit(ret); self.mark(after); return self.contextProperty("v"); } case "ClassExpression": return finish(self.explodeClass(path)); default: throw new Error("unknown Expression of type " + JSON.stringify(expr.type)); } } explodeClass(path) { const explodingChildren = []; if (path.node.superClass) { explodingChildren.push(path.get("superClass")); } path.get("body.body").forEach(member => { if (member.node.computed) { explodingChildren.push(member.get("key")); } }); const hasLeapingChildren = explodingChildren.some(child => containsLeap(child.node)); for (let i = 0; i < explodingChildren.length; i++) { const child = explodingChildren[i]; const isLast = i === explodingChildren.length - 1; if (isLast) { child.replaceWith(this.explodeExpression(child)); } else { child.replaceWith(this.explodeViaTempVar(null, child, hasLeapingChildren)); } } return path.node; } } function replaceShorthandObjectMethod(path) { if (!path.node || !types.isFunction(path.node)) { throw new Error("replaceShorthandObjectMethod can only be called on Function AST node paths."); } if (!types.isObjectMethod(path.node)) { return path; } if (!path.node.generator) { return path; } const parameters = path.node.params.map(function (param) { return types.cloneNode(param); }); const functionExpression = types.functionExpression(null, parameters, types.cloneNode(path.node.body), path.node.generator, path.node.async); return path.replaceWith(types.objectProperty(types.cloneNode(path.node.key), functionExpression, path.node.computed, false))[0].get("value"); } const getVisitor = () => traverse.explode({ Method(path, state) { const node = path.node; if (!shouldRegenerate(node, state)) return; const container = types.functionExpression(null, [], types.cloneNode(node.body, false), node.generator, node.async); path.get("body").set("body", [types.returnStatement(types.callExpression(container, []))]); node.async = false; node.generator = false; path.get("body.body.0.argument.callee").unwrapFunctionEnvironment(); }, Function: { exit(_path, state) { let path = _path; let node = path.node; if (!shouldRegenerate(node, state)) return; path = replaceShorthandObjectMethod(path); node = path.node; const contextId = path.scope.generateUidIdentifier("context"); const argsId = path.scope.generateUidIdentifier("args"); path.ensureBlock(); const bodyBlockPath = path.get("body"); bodyBlockPath.traverse(functionSentVisitor, { context: contextId }); const outerBody = []; const innerBody = []; bodyBlockPath.get("body").forEach(function (childPath) { const node = childPath.node; if (types.isExpressionStatement(node) && types.isStringLiteral(node.expression)) { outerBody.push(node); } else if (node?._blockHoist != null) { outerBody.push(node); } else { innerBody.push(node); } }); if (outerBody.length > 0) { bodyBlockPath.node.body = innerBody; } const outerFnExpr = getOuterFnExpr(this, path); types.assertIdentifier(node.id); const vars = hoist(path); const context = { usesThis: false, usesArguments: false, getArgsId: () => types.cloneNode(argsId) }; path.traverse(argumentsThisVisitor, context); if (context.usesArguments) { vars.push(types.variableDeclarator(types.cloneNode(argsId), types.identifier("arguments"))); } const emitter = new Emitter(contextId, path.scope, vars, this); emitter.explode(path.get("body")); if (vars.length > 0) { outerBody.push(types.variableDeclaration("var", vars)); } const wrapArgs = [emitter.getContextFunction()]; const tryLocsList = emitter.getTryLocsList(); if (node.generator) { wrapArgs.push(outerFnExpr); } else if (context.usesThis || tryLocsList || node.async) { wrapArgs.push(types.nullLiteral()); } if (context.usesThis) { wrapArgs.push(types.thisExpression()); } else if (tryLocsList || node.async) { wrapArgs.push(types.nullLiteral()); } if (tryLocsList) { wrapArgs.push(tryLocsList); } else if (node.async) { wrapArgs.push(types.nullLiteral()); } if (node.async) { let currentScope = path.scope; do { if (currentScope.hasOwnBinding("Promise")) currentScope.rename("Promise"); } while (currentScope = currentScope.parent); wrapArgs.push(types.identifier("Promise")); } const wrapCall = types.callExpression(!node.async ? types.memberExpression(types.callExpression(this.addHelper("regenerator"), []), types.identifier("w")) : node.generator ? this.addHelper("regeneratorAsyncGen") : this.addHelper("regeneratorAsync"), wrapArgs); outerBody.push(types.returnStatement(wrapCall)); node.body = types.blockStatement(outerBody); path.get("body.body").forEach(p => p.scope.registerDeclaration(p)); const oldDirectives = bodyBlockPath.node.directives; if (oldDirectives) { node.body.directives = oldDirectives; } const wasGeneratorFunction = node.generator; if (wasGeneratorFunction) { node.generator = false; } if (node.async) { node.async = false; } if (wasGeneratorFunction && types.isExpression(node)) { path.replaceWith(types.callExpression(types.memberExpression(types.callExpression(this.addHelper("regenerator"), []), types.identifier("m")), [node])); path.addComment("leading", "#__PURE__"); } const insertedLocs = emitter.getInsertedLocs(); path.traverse({ NumericLiteral(path) { if (!insertedLocs.has(path.node)) { return; } path.replaceWith(types.numericLiteral(path.node.value)); } }); path.requeue(); } } }); function shouldRegenerate(node, state) { if (node.generator) { if (node.async) { return state.opts.asyncGenerators !== false; } else { return state.opts.generators !== false; } } else if (node.async) { return state.opts.async !== false; } else { return false; } } function getOuterFnExpr(state, funPath) { const node = funPath.node; types.assertFunction(node); if (!node.id) { node.id = funPath.scope.parent.generateUidIdentifier("callee"); } if (node.generator && types.isFunctionDeclaration(node)) { return getMarkedFunctionId(state, funPath); } return types.cloneNode(node.id); } const markInfo = new WeakMap(); function getMarkInfo(node) { if (!markInfo.has(node)) { markInfo.set(node, {}); } return markInfo.get(node); } function getMarkedFunctionId(state, funPath) { const node = funPath.node; types.assertIdentifier(node.id); const blockPath = funPath.findParent(function (path) { return path.isProgram() || path.isBlockStatement(); }); if (!blockPath) { return node.id; } const block = blockPath.node; assert.ok(Array.isArray(block.body)); const info = getMarkInfo(block); if (!info.decl) { info.decl = types.variableDeclaration("var", []); blockPath.unshiftContainer("body", info.decl); info.declPath = blockPath.get("body.0"); } assert.strictEqual(info.declPath.node, info.decl); const markedId = blockPath.scope.generateUidIdentifier("marked"); const markCallExp = types.callExpression(types.memberExpression(types.callExpression(state.addHelper("regenerator"), []), types.identifier("m")), [types.cloneNode(node.id)]); const index = info.decl.declarations.push(types.variableDeclarator(markedId, markCallExp)) - 1; const markCallExpPath = info.declPath.get("declarations." + index + ".init"); assert.strictEqual(markCallExpPath.node, markCallExp); markCallExpPath.addComment("leading", "#__PURE__"); return types.cloneNode(markedId); } const argumentsThisVisitor = { "FunctionExpression|FunctionDeclaration|Method": function (path) { path.skip(); }, Identifier: function (path, state) { if (path.node.name === "arguments" && isReference(path)) { path.replaceWith(state.getArgsId()); state.usesArguments = true; } }, ThisExpression: function (path, state) { state.usesThis = true; } }; const functionSentVisitor = { MetaProperty(path, state) { const { node } = path; if (node.meta.name === "function" && node.property.name === "sent") { path.replaceWith(types.memberExpression(types.cloneNode(state.context), types.identifier("v"))); } } }; const index = declare(({ assertVersion }) => { assertVersion("^7.0.0-0 || ^8.0.0"); return { name: "transform-regenerator", visitor: getVisitor() }; }); export { index as default }; //# sourceMappingURL=index.js.map