Use regenerator-transform to implement babel-plugin-transform-regenerator (#4881)
* Use regenerator-transform to implement babel-plugin-transform-regenerator. Though I believe this is safe to merge, you may want to wait to merge it until https://github.com/facebook/regenerator/pull/259 is merged. That pull request also provides more explanation of why I think moving the implementation of Regenerator back to its original repository is a good idea. I'm happy to compromise on anything necessary to make this happen. Note that the regenerator-transform version is pegged to an exact version (0.9.7). Past experience has taught me that releasing new versions of Regenerator is almost impossible if client projects are allowed to pull in new patch versions automatically. I will happily submit a pull request whenever a new version is released. * Remove never-used babel-plugin-transform-regenerator/.test directory. * regenerator-transform to 0.9.8
This commit is contained in:
parent
73ff13f326
commit
16c84fb353
@ -1,493 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
|
||||
* additional grant of patent rights can be found in the PATENTS file in
|
||||
* the same directory.
|
||||
*/
|
||||
|
||||
var assert = require("assert");
|
||||
|
||||
describe("async functions and await expressions", function() {
|
||||
Promise = require("promise");
|
||||
|
||||
describe("regeneratorRuntime", function() {
|
||||
it("should be defined globally", function() {
|
||||
var global = Function("return this")();
|
||||
assert.ok("regeneratorRuntime" in global);
|
||||
assert.strictEqual(global.regeneratorRuntime, regeneratorRuntime);
|
||||
});
|
||||
|
||||
it("should have a .wrap method", function() {
|
||||
assert.strictEqual(typeof regeneratorRuntime.wrap, "function");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Promise", function() {
|
||||
it("should be defined globally", function() {
|
||||
var global = Function("return this")();
|
||||
assert.ok("Promise" in global);
|
||||
assert.strictEqual(global.Promise, Promise);
|
||||
});
|
||||
|
||||
it("should be a function", function() {
|
||||
assert.strictEqual(typeof Promise, "function");
|
||||
});
|
||||
});
|
||||
|
||||
describe("no-await async function", function() {
|
||||
it("should return a Promise", function(done) {
|
||||
var called = false;
|
||||
|
||||
async function noAwait(value) {
|
||||
called = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
var promise = noAwait("asdf");
|
||||
assert.strictEqual(called, true);
|
||||
|
||||
promise.then(function(value) {
|
||||
assert.strictEqual(called, true);
|
||||
assert.strictEqual(value, "asdf");
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("one-await async function", function() {
|
||||
it("should finish asynchronously", function(done) {
|
||||
var flag1 = false;
|
||||
var flag2 = false;
|
||||
|
||||
async function oneAwait(value) {
|
||||
flag1 = true;
|
||||
var result = await value;
|
||||
flag2 = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
var promise = oneAwait("asdf");
|
||||
assert.strictEqual(flag1, true);
|
||||
assert.strictEqual(flag2, false);
|
||||
|
||||
promise.then(function(value) {
|
||||
assert.strictEqual(flag2, true);
|
||||
assert.strictEqual(value, "asdf");
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("nested async function calls", function() {
|
||||
it("should evaluate in the right order", function(done) {
|
||||
var markers = [];
|
||||
|
||||
async function innerMost(marker) {
|
||||
markers.push(marker);
|
||||
return await marker;
|
||||
}
|
||||
|
||||
async function inner(marker) {
|
||||
markers.push(marker);
|
||||
|
||||
assert.strictEqual(
|
||||
await innerMost(marker + 1),
|
||||
marker + 1
|
||||
);
|
||||
|
||||
markers.push(marker + 2);
|
||||
|
||||
assert.strictEqual(
|
||||
await innerMost(marker + 3),
|
||||
marker + 3
|
||||
);
|
||||
|
||||
markers.push(marker + 4);
|
||||
}
|
||||
|
||||
async function outer() {
|
||||
markers.push(0);
|
||||
await inner(1);
|
||||
markers.push(6);
|
||||
await inner(7);
|
||||
markers.push(12);
|
||||
}
|
||||
|
||||
outer().then(function() {
|
||||
var expected = [];
|
||||
for (var i = 0; i <= 12; ++i)
|
||||
expected.push(i);
|
||||
assert.deepEqual(markers, expected);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("dependent promises", function() {
|
||||
it("should be awaitable out of order", function(done) {
|
||||
async function outer(value) {
|
||||
var resolved = false;
|
||||
var p1 = new Promise(function(resolve) {
|
||||
setTimeout(function() {
|
||||
resolve(value + 1);
|
||||
resolved = true;
|
||||
}, 0);
|
||||
});
|
||||
|
||||
assert.strictEqual(resolved, false);
|
||||
|
||||
var v2 = await p1.then(function(value) {
|
||||
return value + 1;
|
||||
});
|
||||
|
||||
assert.strictEqual(resolved, true);
|
||||
|
||||
var v1 = await p1;
|
||||
|
||||
return [v1, v2];
|
||||
}
|
||||
|
||||
outer(1).then(function(pair) {
|
||||
assert.deepEqual(pair, [2, 3]);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe("rejected promises", function() {
|
||||
it("should cause await expressions to throw", function(done) {
|
||||
var error = new Error("rejected");
|
||||
|
||||
async function f(arg) {
|
||||
try {
|
||||
return await arg;
|
||||
} catch (e) {
|
||||
assert.strictEqual(e, error);
|
||||
return "did throw";
|
||||
}
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
f(Promise.reject(error)),
|
||||
f(Promise.resolve("did not throw"))
|
||||
]).then(function(results) {
|
||||
assert.deepEqual(results, [
|
||||
"did throw",
|
||||
"did not throw"
|
||||
]);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it("should be returned by exceptional async functions", function(done) {
|
||||
var error = new Error("rejected");
|
||||
|
||||
async function e(arg) {
|
||||
if (arg) {
|
||||
throw arg;
|
||||
}
|
||||
return "did not throw";
|
||||
}
|
||||
|
||||
async function f(arg) {
|
||||
return await e(arg);
|
||||
}
|
||||
|
||||
async function g(arg) {
|
||||
return await f(arg);
|
||||
}
|
||||
|
||||
async function h(arg) {
|
||||
return await Promise.all([
|
||||
g(arg),
|
||||
Promise.resolve("dummy")
|
||||
]);
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
h(error).then(function() {
|
||||
done(new Error("should not have resolved"));
|
||||
}, function(e) {
|
||||
assert.strictEqual(e, error);
|
||||
return "ok1";
|
||||
}),
|
||||
h(null).then(function(result) {
|
||||
assert.deepEqual(result, [
|
||||
"did not throw",
|
||||
"dummy"
|
||||
]);
|
||||
return "ok2";
|
||||
})
|
||||
]).then(function(results) {
|
||||
assert.deepEqual(results, ["ok1", "ok2"]);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it("should propagate failure when returned", function() {
|
||||
var rejection = new Error("rejection");
|
||||
|
||||
async function f() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
reject(rejection);
|
||||
});
|
||||
}
|
||||
|
||||
return f().then(function(result) {
|
||||
assert.ok(false, "should have been rejected");
|
||||
}, function(error) {
|
||||
assert.strictEqual(error, rejection);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("async function expressions", function() {
|
||||
it("should be allowed", function(done) {
|
||||
(async function(arg) {
|
||||
return await arg;
|
||||
})(Promise.resolve(1234)).then(function(value) {
|
||||
assert.strictEqual(value, 1234);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("async generator functions", function() {
|
||||
it("should return a working AsyncIterator", function() {
|
||||
var markers = [];
|
||||
|
||||
async function *gen(arg) {
|
||||
markers.push(0);
|
||||
var sent = yield arg;
|
||||
markers.push(1);
|
||||
var result = await sent;
|
||||
markers.push(2);
|
||||
assert.strictEqual(await (yield "second"), "sent after second");
|
||||
markers.push(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
var iter = gen("initial argument");
|
||||
assert.deepEqual(markers, []);
|
||||
|
||||
var firstPromise = iter.next();
|
||||
assert.deepEqual(markers, [0]);
|
||||
|
||||
return firstPromise.then(function(firstResult) {
|
||||
assert.deepEqual(firstResult, {
|
||||
value: "initial argument",
|
||||
done: false
|
||||
});
|
||||
|
||||
assert.deepEqual(markers, [0]);
|
||||
|
||||
return iter.next(new Promise(function(resolve) {
|
||||
setTimeout(resolve, 100);
|
||||
}).then(function() {
|
||||
assert.deepEqual(markers, [0, 1]);
|
||||
return "will become final result";
|
||||
}));
|
||||
|
||||
}).then(function(secondResult) {
|
||||
assert.deepEqual(secondResult, {
|
||||
value: "second",
|
||||
done: false
|
||||
});
|
||||
|
||||
assert.deepEqual(markers, [0, 1, 2]);
|
||||
|
||||
return iter.next("sent after second");
|
||||
|
||||
}).then(function(finalResult) {
|
||||
assert.deepEqual(markers, [0, 1, 2, 3]);
|
||||
assert.deepEqual(finalResult, {
|
||||
value: "will become final result",
|
||||
done: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should keep results in order", function() {
|
||||
async function *range(limit) {
|
||||
var before = [];
|
||||
var after = [];
|
||||
for (var i = 0; i < limit; ++i) {
|
||||
before.push(i);
|
||||
yield i;
|
||||
after.push(i);
|
||||
}
|
||||
assert.deepEqual(before, after);
|
||||
return before;
|
||||
}
|
||||
|
||||
var limit = 10;
|
||||
var iter = range(limit);
|
||||
var promises = [];
|
||||
var results = [];
|
||||
|
||||
for (var i = 0; i < limit; ++i) {
|
||||
var promise = iter.next();
|
||||
promises.push(promise);
|
||||
|
||||
promise.then(function(result) {
|
||||
assert.strictEqual(result.done, false);
|
||||
results.push(result);
|
||||
});
|
||||
}
|
||||
|
||||
assert.deepEqual(results, []);
|
||||
|
||||
return Promise.all(promises).then(function(promiseResults) {
|
||||
assert.deepEqual(results, promiseResults);
|
||||
|
||||
return iter.next();
|
||||
|
||||
}).then(function(finalResult) {
|
||||
assert.deepEqual(results.map(function(result) {
|
||||
return result.value;
|
||||
}), finalResult.value);
|
||||
|
||||
assert.strictEqual(finalResult.done, true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should be able to handle many awaits", function() {
|
||||
var awaitCount = 0;
|
||||
|
||||
function countAwait(i) {
|
||||
return Promise.resolve(i).then(function() {
|
||||
++awaitCount;
|
||||
});
|
||||
}
|
||||
|
||||
async function *gen(limit) {
|
||||
await countAwait(0);
|
||||
yield 1;
|
||||
await countAwait(2);
|
||||
await countAwait(3);
|
||||
yield 4;
|
||||
await countAwait(5);
|
||||
await countAwait(6);
|
||||
await countAwait(7);
|
||||
yield 8;
|
||||
for (var i = 0; i < limit; ++i) {
|
||||
await countAwait(i);
|
||||
}
|
||||
return "done";
|
||||
}
|
||||
|
||||
var iter = gen(100);
|
||||
|
||||
return iter.next().then(function(result) {
|
||||
assert.strictEqual(awaitCount, 1);
|
||||
|
||||
assert.deepEqual(result, {
|
||||
value: 1,
|
||||
done: false
|
||||
});
|
||||
|
||||
return iter.next();
|
||||
|
||||
}).then(function(result) {
|
||||
assert.strictEqual(awaitCount, 3);
|
||||
|
||||
assert.deepEqual(result, {
|
||||
value: 4,
|
||||
done: false
|
||||
});
|
||||
|
||||
return iter.next();
|
||||
|
||||
}).then(function(result) {
|
||||
assert.strictEqual(awaitCount, 6);
|
||||
|
||||
assert.deepEqual(result, {
|
||||
value: 8,
|
||||
done: false
|
||||
});
|
||||
|
||||
return iter.next();
|
||||
|
||||
}).then(function(result) {
|
||||
assert.strictEqual(awaitCount, 6 + 100);
|
||||
|
||||
assert.deepEqual(result, {
|
||||
value: "done",
|
||||
done: true
|
||||
});
|
||||
|
||||
return iter.next();
|
||||
|
||||
}).then(function(result) {
|
||||
assert.deepEqual(result, {
|
||||
value: void 0,
|
||||
done: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should not propagate exceptions between iterations", function() {
|
||||
async function *gen() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
}
|
||||
|
||||
var iter = gen();
|
||||
|
||||
return iter.next().then(function(result) {
|
||||
assert.deepEqual(result, {
|
||||
value: 1,
|
||||
done: false
|
||||
});
|
||||
|
||||
return iter.throw(new Error("thrown from first yield"));
|
||||
|
||||
}).then(function() {
|
||||
throw new Error("should have thrown");
|
||||
|
||||
}, function(error) {
|
||||
assert.strictEqual(error.message, "thrown from first yield");
|
||||
return iter.next();
|
||||
|
||||
}).then(function(result) {
|
||||
assert.deepEqual(result, {
|
||||
value: void 0,
|
||||
done: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should allow yielding a rejected Promise", function() {
|
||||
var yielded = new Error("yielded rejection");
|
||||
var returned = new Error("returned rejection");
|
||||
|
||||
async function *gen() {
|
||||
assert.strictEqual(yield Promise.reject(yielded), "first sent");
|
||||
assert.strictEqual(yield "middle", "second sent");
|
||||
return Promise.reject(returned);
|
||||
}
|
||||
|
||||
var iter = gen();
|
||||
|
||||
return iter.next().then(function(result) {
|
||||
assert.ok(false, "should have yielded a rejected Promise");
|
||||
}, function(error) {
|
||||
assert.strictEqual(error, yielded);
|
||||
return iter.next("first sent");
|
||||
}).then(function(result) {
|
||||
assert.deepEqual(result, {
|
||||
value: "middle",
|
||||
done: false
|
||||
});
|
||||
return iter.next("second sent");
|
||||
}).then(function(result) {
|
||||
assert.ok(false, "should have returned a rejected Promise");
|
||||
}, function(error) {
|
||||
assert.strictEqual(error, returned);
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@ -7,9 +7,7 @@
|
||||
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator",
|
||||
"main": "lib/index.js",
|
||||
"dependencies": {
|
||||
"babel-types": "^6.16.0",
|
||||
"babel-runtime": "^6.9.0",
|
||||
"private": "~0.1.5"
|
||||
"regenerator-transform": "0.9.8"
|
||||
},
|
||||
"license": "BSD",
|
||||
"devDependencies": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,150 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
|
||||
* additional grant of patent rights can be found in the PATENTS file in
|
||||
* the same directory.
|
||||
*/
|
||||
|
||||
import * as t from "babel-types";
|
||||
let hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
// The hoist function takes a FunctionExpression or FunctionDeclaration
|
||||
// and replaces any Declaration nodes in its body with assignments, then
|
||||
// returns a VariableDeclaration containing just the names of the removed
|
||||
// declarations.
|
||||
exports.hoist = function(funPath) {
|
||||
t.assertFunction(funPath.node);
|
||||
|
||||
let vars = {};
|
||||
|
||||
function varDeclToExpr(vdec, includeIdentifiers) {
|
||||
t.assertVariableDeclaration(vdec);
|
||||
// TODO assert.equal(vdec.kind, "var");
|
||||
let exprs = [];
|
||||
|
||||
vdec.declarations.forEach(function(dec) {
|
||||
// Note: We duplicate 'dec.id' here to ensure that the variable declaration IDs don't
|
||||
// have the same 'loc' value, since that can make sourcemaps and retainLines behave poorly.
|
||||
vars[dec.id.name] = t.identifier(dec.id.name);
|
||||
|
||||
if (dec.init) {
|
||||
exprs.push(t.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 t.sequenceExpression(exprs);
|
||||
}
|
||||
|
||||
funPath.get("body").traverse({
|
||||
VariableDeclaration: {
|
||||
exit: function(path) {
|
||||
let expr = varDeclToExpr(path.node, false);
|
||||
if (expr === null) {
|
||||
path.remove();
|
||||
} else {
|
||||
// We don't need to traverse this expression any further because
|
||||
// there can't be any new declarations inside an expression.
|
||||
path.replaceWith(t.expressionStatement(expr));
|
||||
}
|
||||
|
||||
// Since the original node has been either removed or replaced,
|
||||
// avoid traversing it any further.
|
||||
path.skip();
|
||||
}
|
||||
},
|
||||
|
||||
ForStatement: function(path) {
|
||||
let init = path.node.init;
|
||||
if (t.isVariableDeclaration(init)) {
|
||||
path.get("init").replaceWith(varDeclToExpr(init, false));
|
||||
}
|
||||
},
|
||||
|
||||
ForXStatement: function(path) {
|
||||
let left = path.get("left");
|
||||
if (left.isVariableDeclaration()) {
|
||||
left.replaceWith(varDeclToExpr(left.node, true));
|
||||
}
|
||||
},
|
||||
|
||||
FunctionDeclaration: function(path) {
|
||||
let node = path.node;
|
||||
vars[node.id.name] = node.id;
|
||||
|
||||
let assignment = t.expressionStatement(
|
||||
t.assignmentExpression(
|
||||
"=",
|
||||
node.id,
|
||||
t.functionExpression(
|
||||
node.id,
|
||||
node.params,
|
||||
node.body,
|
||||
node.generator,
|
||||
node.expression
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (path.parentPath.isBlockStatement()) {
|
||||
// Insert the assignment form before the first statement in the
|
||||
// enclosing block.
|
||||
path.parentPath.unshiftContainer("body", assignment);
|
||||
|
||||
// Remove the function declaration now that we've inserted the
|
||||
// equivalent assignment form at the beginning of the block.
|
||||
path.remove();
|
||||
} else {
|
||||
// If the parent node is not a block statement, then we can just
|
||||
// replace the declaration with the equivalent assignment form
|
||||
// without worrying about hoisting it.
|
||||
path.replaceWith(assignment);
|
||||
}
|
||||
|
||||
// Don't hoist variables out of inner functions.
|
||||
path.skip();
|
||||
},
|
||||
|
||||
FunctionExpression: function(path) {
|
||||
// Don't descend into nested function expressions.
|
||||
path.skip();
|
||||
}
|
||||
});
|
||||
|
||||
let paramNames = {};
|
||||
funPath.get("params").forEach(function(paramPath) {
|
||||
let param = paramPath.node;
|
||||
if (t.isIdentifier(param)) {
|
||||
paramNames[param.name] = param;
|
||||
} else {
|
||||
// Variables declared by destructuring parameter patterns will be
|
||||
// harmlessly re-declared.
|
||||
}
|
||||
});
|
||||
|
||||
let declarations = [];
|
||||
|
||||
Object.keys(vars).forEach(function(name) {
|
||||
if (!hasOwn.call(paramNames, name)) {
|
||||
declarations.push(t.variableDeclarator(vars[name], null));
|
||||
}
|
||||
});
|
||||
|
||||
if (declarations.length === 0) {
|
||||
return null; // Be sure to handle this case!
|
||||
}
|
||||
|
||||
return t.variableDeclaration("var", declarations);
|
||||
};
|
||||
@ -8,6 +8,4 @@
|
||||
* the same directory.
|
||||
*/
|
||||
|
||||
export default function () {
|
||||
return require("./visit");
|
||||
}
|
||||
export default require("regenerator-transform");
|
||||
|
||||
@ -1,174 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
|
||||
* additional grant of patent rights can be found in the PATENTS file in
|
||||
* the same directory.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
import * as t from "babel-types";
|
||||
import { inherits } from "util";
|
||||
|
||||
function Entry() {
|
||||
assert.ok(this instanceof Entry);
|
||||
}
|
||||
|
||||
function FunctionEntry(returnLoc) {
|
||||
Entry.call(this);
|
||||
t.assertLiteral(returnLoc);
|
||||
this.returnLoc = returnLoc;
|
||||
}
|
||||
|
||||
inherits(FunctionEntry, Entry);
|
||||
exports.FunctionEntry = FunctionEntry;
|
||||
|
||||
function LoopEntry(breakLoc, continueLoc, label) {
|
||||
Entry.call(this);
|
||||
|
||||
t.assertLiteral(breakLoc);
|
||||
t.assertLiteral(continueLoc);
|
||||
|
||||
if (label) {
|
||||
t.assertIdentifier(label);
|
||||
} else {
|
||||
label = null;
|
||||
}
|
||||
|
||||
this.breakLoc = breakLoc;
|
||||
this.continueLoc = continueLoc;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
inherits(LoopEntry, Entry);
|
||||
exports.LoopEntry = LoopEntry;
|
||||
|
||||
function SwitchEntry(breakLoc) {
|
||||
Entry.call(this);
|
||||
t.assertLiteral(breakLoc);
|
||||
this.breakLoc = breakLoc;
|
||||
}
|
||||
|
||||
inherits(SwitchEntry, Entry);
|
||||
exports.SwitchEntry = SwitchEntry;
|
||||
|
||||
function TryEntry(firstLoc, catchEntry, finallyEntry) {
|
||||
Entry.call(this);
|
||||
|
||||
t.assertLiteral(firstLoc);
|
||||
|
||||
if (catchEntry) {
|
||||
assert.ok(catchEntry instanceof CatchEntry);
|
||||
} else {
|
||||
catchEntry = null;
|
||||
}
|
||||
|
||||
if (finallyEntry) {
|
||||
assert.ok(finallyEntry instanceof FinallyEntry);
|
||||
} else {
|
||||
finallyEntry = null;
|
||||
}
|
||||
|
||||
// Have to have one or the other (or both).
|
||||
assert.ok(catchEntry || finallyEntry);
|
||||
|
||||
this.firstLoc = firstLoc;
|
||||
this.catchEntry = catchEntry;
|
||||
this.finallyEntry = finallyEntry;
|
||||
}
|
||||
|
||||
inherits(TryEntry, Entry);
|
||||
exports.TryEntry = TryEntry;
|
||||
|
||||
function CatchEntry(firstLoc, paramId) {
|
||||
Entry.call(this);
|
||||
|
||||
t.assertLiteral(firstLoc);
|
||||
t.assertIdentifier(paramId);
|
||||
|
||||
this.firstLoc = firstLoc;
|
||||
this.paramId = paramId;
|
||||
}
|
||||
|
||||
inherits(CatchEntry, Entry);
|
||||
exports.CatchEntry = CatchEntry;
|
||||
|
||||
function FinallyEntry(firstLoc, afterLoc) {
|
||||
Entry.call(this);
|
||||
t.assertLiteral(firstLoc);
|
||||
t.assertLiteral(afterLoc);
|
||||
this.firstLoc = firstLoc;
|
||||
this.afterLoc = afterLoc;
|
||||
}
|
||||
|
||||
inherits(FinallyEntry, Entry);
|
||||
exports.FinallyEntry = FinallyEntry;
|
||||
|
||||
function LabeledEntry(breakLoc, label) {
|
||||
Entry.call(this);
|
||||
|
||||
t.assertLiteral(breakLoc);
|
||||
t.assertIdentifier(label);
|
||||
|
||||
this.breakLoc = breakLoc;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
inherits(LabeledEntry, Entry);
|
||||
exports.LabeledEntry = LabeledEntry;
|
||||
|
||||
function LeapManager(emitter) {
|
||||
assert.ok(this instanceof LeapManager);
|
||||
|
||||
let Emitter = require("./emit").Emitter;
|
||||
assert.ok(emitter instanceof Emitter);
|
||||
|
||||
this.emitter = emitter;
|
||||
this.entryStack = [new FunctionEntry(emitter.finalLoc)];
|
||||
}
|
||||
|
||||
let LMp = LeapManager.prototype;
|
||||
exports.LeapManager = LeapManager;
|
||||
|
||||
LMp.withEntry = function(entry, callback) {
|
||||
assert.ok(entry instanceof Entry);
|
||||
this.entryStack.push(entry);
|
||||
try {
|
||||
callback.call(this.emitter);
|
||||
} finally {
|
||||
let popped = this.entryStack.pop();
|
||||
assert.strictEqual(popped, entry);
|
||||
}
|
||||
};
|
||||
|
||||
LMp._findLeapLocation = function(property, label) {
|
||||
for (let i = this.entryStack.length - 1; i >= 0; --i) {
|
||||
let entry = this.entryStack[i];
|
||||
let loc = entry[property];
|
||||
if (loc) {
|
||||
if (label) {
|
||||
if (entry.label &&
|
||||
entry.label.name === label.name) {
|
||||
return loc;
|
||||
}
|
||||
} else if (entry instanceof LabeledEntry) {
|
||||
// Ignore LabeledEntry entries unless we are actually breaking to
|
||||
// a label.
|
||||
} else {
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
LMp.getBreakLoc = function(label) {
|
||||
return this._findLeapLocation("breakLoc", label);
|
||||
};
|
||||
|
||||
LMp.getContinueLoc = function(label) {
|
||||
return this._findLeapLocation("continueLoc", label);
|
||||
};
|
||||
@ -1,103 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
|
||||
* additional grant of patent rights can be found in the PATENTS file in
|
||||
* the same directory.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
let m = require("private").makeAccessor();
|
||||
import * as t from "babel-types";
|
||||
let hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
function makePredicate(propertyName, knownTypes) {
|
||||
function onlyChildren(node) {
|
||||
t.assertNode(node);
|
||||
|
||||
// Assume no side effects until we find out otherwise.
|
||||
let result = false;
|
||||
|
||||
function check(child) {
|
||||
if (result) {
|
||||
// Do nothing.
|
||||
} else if (Array.isArray(child)) {
|
||||
child.some(check);
|
||||
} else if (t.isNode(child)) {
|
||||
assert.strictEqual(result, false);
|
||||
result = predicate(child);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
let keys = t.VISITOR_KEYS[node.type];
|
||||
if (keys) {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
let child = node[key];
|
||||
check(child);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function predicate(node) {
|
||||
t.assertNode(node);
|
||||
|
||||
let meta = m(node);
|
||||
if (hasOwn.call(meta, propertyName))
|
||||
return meta[propertyName];
|
||||
|
||||
// Certain types are "opaque," which means they have no side
|
||||
// effects or leaps and we don't care about their subexpressions.
|
||||
if (hasOwn.call(opaqueTypes, node.type))
|
||||
return meta[propertyName] = false;
|
||||
|
||||
if (hasOwn.call(knownTypes, node.type))
|
||||
return meta[propertyName] = true;
|
||||
|
||||
return meta[propertyName] = onlyChildren(node);
|
||||
}
|
||||
|
||||
predicate.onlyChildren = onlyChildren;
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
let opaqueTypes = {
|
||||
FunctionExpression: true
|
||||
};
|
||||
|
||||
// These types potentially have side effects regardless of what side
|
||||
// effects their subexpressions have.
|
||||
let sideEffectTypes = {
|
||||
CallExpression: true, // Anything could happen!
|
||||
ForInStatement: true, // Modifies the key variable.
|
||||
UnaryExpression: true, // Think delete.
|
||||
BinaryExpression: true, // Might invoke .toString() or .valueOf().
|
||||
AssignmentExpression: true, // Side-effecting by definition.
|
||||
UpdateExpression: true, // Updates are essentially assignments.
|
||||
NewExpression: true // Similar to CallExpression.
|
||||
};
|
||||
|
||||
// These types are the direct cause of all leaps in control flow.
|
||||
let leapTypes = {
|
||||
YieldExpression: true,
|
||||
BreakStatement: true,
|
||||
ContinueStatement: true,
|
||||
ReturnStatement: true,
|
||||
ThrowStatement: true
|
||||
};
|
||||
|
||||
// All leap types are also side effect types.
|
||||
for (let type in leapTypes) {
|
||||
if (hasOwn.call(leapTypes, type)) {
|
||||
sideEffectTypes[type] = leapTypes[type];
|
||||
}
|
||||
}
|
||||
|
||||
exports.hasSideEffects = makePredicate("hasSideEffects", sideEffectTypes);
|
||||
exports.containsLeap = makePredicate("containsLeap", leapTypes);
|
||||
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
|
||||
* additional grant of patent rights can be found in the PATENTS file in
|
||||
* the same directory.
|
||||
*/
|
||||
|
||||
import * as t from "babel-types";
|
||||
|
||||
export function runtimeProperty(name) {
|
||||
return t.memberExpression(
|
||||
t.identifier("regeneratorRuntime"),
|
||||
t.identifier(name),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
export function isReference(path) {
|
||||
return path.isReferenced() || path.parentPath.isAssignmentExpression({ left: path.node });
|
||||
}
|
||||
@ -1,269 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
|
||||
* additional grant of patent rights can be found in the PATENTS file in
|
||||
* the same directory.
|
||||
*/
|
||||
|
||||
import assert from "assert";
|
||||
import * as t from "babel-types";
|
||||
import { hoist } from "./hoist";
|
||||
import { Emitter } from "./emit";
|
||||
import * as util from "./util";
|
||||
|
||||
let getMarkInfo = require("private").makeAccessor();
|
||||
|
||||
exports.visitor = {
|
||||
Function: {
|
||||
exit: function(path, state) {
|
||||
let node = path.node;
|
||||
|
||||
if (node.generator) {
|
||||
if (node.async) {
|
||||
// Async generator
|
||||
if (state.opts.asyncGenerators === false) return;
|
||||
} else {
|
||||
// Plain generator
|
||||
if (state.opts.generators === false) return;
|
||||
}
|
||||
} else if (node.async) {
|
||||
// Async function
|
||||
if (state.opts.async === false) return;
|
||||
} else {
|
||||
// Not a generator or async function.
|
||||
return;
|
||||
}
|
||||
|
||||
let contextId = path.scope.generateUidIdentifier("context");
|
||||
let argsId = path.scope.generateUidIdentifier("args");
|
||||
|
||||
path.ensureBlock();
|
||||
let bodyBlockPath = path.get("body");
|
||||
|
||||
if (node.async) {
|
||||
bodyBlockPath.traverse(awaitVisitor);
|
||||
}
|
||||
|
||||
bodyBlockPath.traverse(functionSentVisitor, {
|
||||
context: contextId
|
||||
});
|
||||
|
||||
let outerBody = [];
|
||||
let innerBody = [];
|
||||
|
||||
bodyBlockPath.get("body").forEach(function(childPath) {
|
||||
let node = childPath.node;
|
||||
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;
|
||||
}
|
||||
|
||||
let 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);
|
||||
let innerFnId = t.identifier(node.id.name + "$");
|
||||
|
||||
// Turn all declarations into vars, and replace the original
|
||||
// declarations with equivalent assignment expressions.
|
||||
let vars = hoist(path);
|
||||
|
||||
let didRenameArguments = renameArguments(path, argsId);
|
||||
if (didRenameArguments) {
|
||||
vars = vars || t.variableDeclaration("var", []);
|
||||
vars.declarations.push(t.variableDeclarator(
|
||||
argsId, t.identifier("arguments")
|
||||
));
|
||||
}
|
||||
|
||||
let emitter = new Emitter(contextId);
|
||||
emitter.explode(path.get("body"));
|
||||
|
||||
if (vars && vars.declarations.length > 0) {
|
||||
outerBody.push(vars);
|
||||
}
|
||||
|
||||
let wrapArgs = [
|
||||
emitter.getContextFunction(innerFnId),
|
||||
// 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.
|
||||
node.generator ? outerFnExpr : t.nullLiteral(),
|
||||
t.thisExpression()
|
||||
];
|
||||
|
||||
let tryLocsList = emitter.getTryLocsList();
|
||||
if (tryLocsList) {
|
||||
wrapArgs.push(tryLocsList);
|
||||
}
|
||||
|
||||
let wrapCall = t.callExpression(
|
||||
util.runtimeProperty(node.async ? "async" : "wrap"),
|
||||
wrapArgs
|
||||
);
|
||||
|
||||
outerBody.push(t.returnStatement(wrapCall));
|
||||
node.body = t.blockStatement(outerBody);
|
||||
|
||||
let wasGeneratorFunction = node.generator;
|
||||
if (wasGeneratorFunction) {
|
||||
node.generator = false;
|
||||
}
|
||||
|
||||
if (node.async) {
|
||||
node.async = false;
|
||||
}
|
||||
|
||||
if (wasGeneratorFunction && t.isExpression(node)) {
|
||||
path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node]));
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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) {
|
||||
let 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)) {
|
||||
let pp = funPath.findParent(function (path) {
|
||||
return path.isProgram() || path.isBlockStatement();
|
||||
});
|
||||
|
||||
if (!pp) {
|
||||
return node.id;
|
||||
}
|
||||
|
||||
let markDecl = getRuntimeMarkDecl(pp);
|
||||
let markedArray = markDecl.declarations[0].id;
|
||||
let funDeclIdArray = markDecl.declarations[0].init.callee.object;
|
||||
t.assertArrayExpression(funDeclIdArray);
|
||||
|
||||
let index = funDeclIdArray.elements.length;
|
||||
funDeclIdArray.elements.push(node.id);
|
||||
|
||||
return t.memberExpression(
|
||||
markedArray,
|
||||
t.numericLiteral(index),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return node.id;
|
||||
}
|
||||
|
||||
function getRuntimeMarkDecl(blockPath) {
|
||||
let block = blockPath.node;
|
||||
assert.ok(Array.isArray(block.body));
|
||||
|
||||
let info = getMarkInfo(block);
|
||||
if (info.decl) {
|
||||
return info.decl;
|
||||
}
|
||||
|
||||
info.decl = t.variableDeclaration("var", [
|
||||
t.variableDeclarator(
|
||||
blockPath.scope.generateUidIdentifier("marked"),
|
||||
t.callExpression(
|
||||
t.memberExpression(
|
||||
t.arrayExpression([]),
|
||||
t.identifier("map"),
|
||||
false
|
||||
),
|
||||
[util.runtimeProperty("mark")]
|
||||
)
|
||||
)
|
||||
]);
|
||||
|
||||
blockPath.unshiftContainer("body", info.decl);
|
||||
|
||||
return info.decl;
|
||||
}
|
||||
|
||||
function renameArguments(funcPath, argsId) {
|
||||
let state = {
|
||||
didRenameArguments: false,
|
||||
argsId: argsId
|
||||
};
|
||||
|
||||
funcPath.traverse(argumentsVisitor, state);
|
||||
|
||||
// If the traversal replaced any arguments references, then we need to
|
||||
// alias the outer function's arguments binding (be it the implicit
|
||||
// arguments object or some other parameter or variable) to the variable
|
||||
// named by argsId.
|
||||
return state.didRenameArguments;
|
||||
}
|
||||
|
||||
let argumentsVisitor = {
|
||||
"FunctionExpression|FunctionDeclaration": function(path) {
|
||||
path.skip();
|
||||
},
|
||||
|
||||
Identifier: function(path, state) {
|
||||
if (path.node.name === "arguments" && util.isReference(path)) {
|
||||
path.replaceWith(state.argsId);
|
||||
state.didRenameArguments = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let functionSentVisitor = {
|
||||
MetaProperty(path) {
|
||||
let { node } = path;
|
||||
|
||||
if (node.meta.name === "function" && node.property.name === "sent") {
|
||||
path.replaceWith(t.memberExpression(this.context, t.identifier("_sent")));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let awaitVisitor = {
|
||||
Function: function(path) {
|
||||
path.skip(); // Don't descend into nested function scopes.
|
||||
},
|
||||
|
||||
AwaitExpression: function(path) {
|
||||
// Convert await expressions to yield expressions.
|
||||
let 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.
|
||||
path.replaceWith(t.yieldExpression(
|
||||
t.callExpression(
|
||||
util.runtimeProperty("awrap"),
|
||||
[argument]
|
||||
),
|
||||
false
|
||||
));
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user