clean up babel-plugin-transform-regenerator

This commit is contained in:
Sebastian McKenzie 2015-11-09 04:15:51 -08:00
parent f047a593df
commit 6fa85e6f5b
28 changed files with 2575 additions and 7448 deletions

1
.gitignore vendored
View File

@ -15,6 +15,5 @@ dist
/packages/babel-runtime/helpers/*.js
/packages/babel-runtime/regenerator/*.js
/packages/*/lib
!/packages/babel-plugin-transform-regenerator/lib
_babel.github.io
/tests/.browser-build.js

View File

@ -1,12 +0,0 @@
var vm = require("vm");
var fs = require("fs");
var loc = __dirname + "/../dist/browser.js";
suite("browser", function () {
test("sanity check", function () {
if (fs.existsSync(loc)) {
require(loc);
}
});
});

View File

@ -1,11 +0,0 @@
npm-debug.log
node_modules
test/mocha.js
test/mocha.css
test/tests.es5.js
test/async.es5.js
test/tests.browser.js
.idea

View File

@ -1,805 +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;
function noAwait(value) {
return regeneratorRuntime.async(function noAwait$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
called = true;
return _context.abrupt("return", value);
case 2:
case "end":
return _context.stop();
}
}, null, this);
}
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;
function oneAwait(value) {
var result;
return regeneratorRuntime.async(function oneAwait$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
flag1 = true;
_context2.next = 3;
return regeneratorRuntime.awrap(value);
case 3:
result = _context2.sent;
flag2 = true;
return _context2.abrupt("return", result);
case 6:
case "end":
return _context2.stop();
}
}, null, this);
}
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 = [];
function innerMost(marker) {
return regeneratorRuntime.async(function innerMost$(_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
markers.push(marker);
_context3.next = 3;
return regeneratorRuntime.awrap(marker);
case 3:
return _context3.abrupt("return", _context3.sent);
case 4:
case "end":
return _context3.stop();
}
}, null, this);
}
function inner(marker) {
return regeneratorRuntime.async(function inner$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
markers.push(marker);
_context4.t0 = assert;
_context4.next = 4;
return regeneratorRuntime.awrap(innerMost(marker + 1));
case 4:
_context4.t1 = _context4.sent;
_context4.t2 = marker + 1;
_context4.t0.strictEqual.call(_context4.t0, _context4.t1, _context4.t2);
markers.push(marker + 2);
_context4.t3 = assert;
_context4.next = 11;
return regeneratorRuntime.awrap(innerMost(marker + 3));
case 11:
_context4.t4 = _context4.sent;
_context4.t5 = marker + 3;
_context4.t3.strictEqual.call(_context4.t3, _context4.t4, _context4.t5);
markers.push(marker + 4);
case 15:
case "end":
return _context4.stop();
}
}, null, this);
}
function outer() {
return regeneratorRuntime.async(function outer$(_context5) {
while (1) switch (_context5.prev = _context5.next) {
case 0:
markers.push(0);
_context5.next = 3;
return regeneratorRuntime.awrap(inner(1));
case 3:
markers.push(6);
_context5.next = 6;
return regeneratorRuntime.awrap(inner(7));
case 6:
markers.push(12);
case 7:
case "end":
return _context5.stop();
}
}, null, this);
}
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) {
function outer(value) {
var resolved, p1, v2, v1;
return regeneratorRuntime.async(function outer$(_context6) {
while (1) switch (_context6.prev = _context6.next) {
case 0:
resolved = false;
p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve(value + 1);
resolved = true;
}, 0);
});
assert.strictEqual(resolved, false);
_context6.next = 5;
return regeneratorRuntime.awrap(p1.then(function (value) {
return value + 1;
}));
case 5:
v2 = _context6.sent;
assert.strictEqual(resolved, true);
_context6.next = 9;
return regeneratorRuntime.awrap(p1);
case 9:
v1 = _context6.sent;
return _context6.abrupt("return", [v1, v2]);
case 11:
case "end":
return _context6.stop();
}
}, null, this);
}
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");
function f(arg) {
return regeneratorRuntime.async(function f$(_context7) {
while (1) switch (_context7.prev = _context7.next) {
case 0:
_context7.prev = 0;
_context7.next = 3;
return regeneratorRuntime.awrap(arg);
case 3:
return _context7.abrupt("return", _context7.sent);
case 6:
_context7.prev = 6;
_context7.t0 = _context7["catch"](0);
assert.strictEqual(_context7.t0, error);
return _context7.abrupt("return", "did throw");
case 10:
case "end":
return _context7.stop();
}
}, null, this, [[0, 6]]);
}
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");
function e(arg) {
return regeneratorRuntime.async(function e$(_context8) {
while (1) switch (_context8.prev = _context8.next) {
case 0:
if (!arg) {
_context8.next = 2;
break;
}
throw arg;
case 2:
return _context8.abrupt("return", "did not throw");
case 3:
case "end":
return _context8.stop();
}
}, null, this);
}
function f(arg) {
return regeneratorRuntime.async(function f$(_context9) {
while (1) switch (_context9.prev = _context9.next) {
case 0:
_context9.next = 2;
return regeneratorRuntime.awrap(e(arg));
case 2:
return _context9.abrupt("return", _context9.sent);
case 3:
case "end":
return _context9.stop();
}
}, null, this);
}
function g(arg) {
return regeneratorRuntime.async(function g$(_context10) {
while (1) switch (_context10.prev = _context10.next) {
case 0:
_context10.next = 2;
return regeneratorRuntime.awrap(f(arg));
case 2:
return _context10.abrupt("return", _context10.sent);
case 3:
case "end":
return _context10.stop();
}
}, null, this);
}
function h(arg) {
return regeneratorRuntime.async(function h$(_context11) {
while (1) switch (_context11.prev = _context11.next) {
case 0:
_context11.next = 2;
return regeneratorRuntime.awrap(Promise.all([g(arg), Promise.resolve("dummy")]));
case 2:
return _context11.abrupt("return", _context11.sent);
case 3:
case "end":
return _context11.stop();
}
}, null, this);
}
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");
function f() {
return regeneratorRuntime.async(function f$(_context12) {
while (1) switch (_context12.prev = _context12.next) {
case 0:
return _context12.abrupt("return", new Promise(function (resolve, reject) {
reject(rejection);
}));
case 1:
case "end":
return _context12.stop();
}
}, null, this);
}
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) {
(function _callee(arg) {
return regeneratorRuntime.async(function _callee$(_context13) {
while (1) switch (_context13.prev = _context13.next) {
case 0:
_context13.next = 2;
return regeneratorRuntime.awrap(arg);
case 2:
return _context13.abrupt("return", _context13.sent);
case 3:
case "end":
return _context13.stop();
}
}, null, this);
})(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 _marked = [gen].map(regeneratorRuntime.mark);
var markers = [];
function gen(arg) {
var sent, result;
return regeneratorRuntime.async(function gen$(_context14) {
while (1) switch (_context14.prev = _context14.next) {
case 0:
markers.push(0);
_context14.next = 3;
return arg;
case 3:
sent = _context14.sent;
markers.push(1);
_context14.next = 7;
return regeneratorRuntime.awrap(sent);
case 7:
result = _context14.sent;
markers.push(2);
_context14.t0 = assert;
_context14.t1 = regeneratorRuntime;
_context14.next = 13;
return "second";
case 13:
_context14.t2 = _context14.sent;
_context14.next = 16;
return _context14.t1.awrap.call(_context14.t1, _context14.t2);
case 16:
_context14.t3 = _context14.sent;
_context14.t0.strictEqual.call(_context14.t0, _context14.t3, "sent after second");
markers.push(3);
return _context14.abrupt("return", result);
case 20:
case "end":
return _context14.stop();
}
}, _marked[0], this);
}
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 () {
var _marked2 = [range].map(regeneratorRuntime.mark);
function range(limit) {
var before, after, i;
return regeneratorRuntime.async(function range$(_context15) {
while (1) switch (_context15.prev = _context15.next) {
case 0:
before = [];
after = [];
i = 0;
case 3:
if (!(i < limit)) {
_context15.next = 11;
break;
}
before.push(i);
_context15.next = 7;
return i;
case 7:
after.push(i);
case 8:
++i;
_context15.next = 3;
break;
case 11:
assert.deepEqual(before, after);
return _context15.abrupt("return", before);
case 13:
case "end":
return _context15.stop();
}
}, _marked2[0], this);
}
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 _marked3 = [gen].map(regeneratorRuntime.mark);
var awaitCount = 0;
function countAwait(i) {
return Promise.resolve(i).then(function () {
++awaitCount;
});
}
function gen(limit) {
var i;
return regeneratorRuntime.async(function gen$(_context16) {
while (1) switch (_context16.prev = _context16.next) {
case 0:
_context16.next = 2;
return regeneratorRuntime.awrap(countAwait(0));
case 2:
_context16.next = 4;
return 1;
case 4:
_context16.next = 6;
return regeneratorRuntime.awrap(countAwait(2));
case 6:
_context16.next = 8;
return regeneratorRuntime.awrap(countAwait(3));
case 8:
_context16.next = 10;
return 4;
case 10:
_context16.next = 12;
return regeneratorRuntime.awrap(countAwait(5));
case 12:
_context16.next = 14;
return regeneratorRuntime.awrap(countAwait(6));
case 14:
_context16.next = 16;
return regeneratorRuntime.awrap(countAwait(7));
case 16:
_context16.next = 18;
return 8;
case 18:
i = 0;
case 19:
if (!(i < limit)) {
_context16.next = 25;
break;
}
_context16.next = 22;
return regeneratorRuntime.awrap(countAwait(i));
case 22:
++i;
_context16.next = 19;
break;
case 25:
return _context16.abrupt("return", "done");
case 26:
case "end":
return _context16.stop();
}
}, _marked3[0], this);
}
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 () {
var _marked4 = [gen].map(regeneratorRuntime.mark);
function gen() {
return regeneratorRuntime.async(function gen$(_context17) {
while (1) switch (_context17.prev = _context17.next) {
case 0:
_context17.next = 2;
return 1;
case 2:
_context17.next = 4;
return 2;
case 4:
case "end":
return _context17.stop();
}
}, _marked4[0], this);
}
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 _marked5 = [gen].map(regeneratorRuntime.mark);
var yielded = new Error("yielded rejection");
var returned = new Error("returned rejection");
function gen() {
return regeneratorRuntime.async(function gen$(_context18) {
while (1) switch (_context18.prev = _context18.next) {
case 0:
_context18.t0 = assert;
_context18.next = 3;
return Promise.reject(yielded);
case 3:
_context18.t1 = _context18.sent;
_context18.t0.strictEqual.call(_context18.t0, _context18.t1, "first sent");
_context18.t2 = assert;
_context18.next = 8;
return "middle";
case 8:
_context18.t3 = _context18.sent;
_context18.t2.strictEqual.call(_context18.t2, _context18.t3, "second sent");
return _context18.abrupt("return", Promise.reject(returned));
case 11:
case "end":
return _context18.stop();
}
}, _marked5[0], this);
}
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);
});
});
});

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Mocha</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="tests.browser.js"></script>
<script>
mocha.run();
</script>
</body>
</html>

View File

@ -1,3 +0,0 @@
function asdf() {
return "no generators or a-s-y-n-c functions to see here";
}

View File

@ -1,154 +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 fs = require("fs");
var path = require("path");
var semver = require("semver");
var spawn = require("child_process").spawn;
var regenerator = require("../main");
var mochaDir = path.dirname(require.resolve("mocha"));
function convert(es6File, es5File, callback) {
fs.readFile(es6File, "utf-8", function(err, es6) {
if (err) {
return callback(err);
}
fs.writeFile(es5File, regenerator.compile(es6).code, callback);
});
}
function bundle(es5Files, browserFile, callback) {
var bundle = require("browserify")();
es5Files.forEach(bundle.add, bundle);
bundle.bundle(function(err, src) {
if (err) {
return callback(err);
}
fs.writeFile(browserFile, src, callback);
});
}
var queue = [];
function enqueue(cmd, args, quiet) {
queue.push({
cmd: cmd,
args: args || [],
quiet: !!quiet
});
}
function flush() {
var entry = queue.shift();
if (entry) {
var cmd = entry.cmd;
if (typeof cmd === "function") {
cmd.apply(null, entry.args.concat(asyncCallback));
} else {
spawn(cmd, entry.args, {
stdio: [
process.stdin,
entry.quiet ? "ignore" : process.stdout,
process.stderr
]
}).on("exit", asyncCallback);
}
}
}
function asyncCallback(err) {
if (err) {
console.error("process exited abnormally:", err);
process.exit(typeof err === "number" ? err : -1);
} else {
process.nextTick(flush);
}
}
function makeMochaCopyFunction(fileName) {
return function copy(callback) {
var src = path.join(mochaDir, fileName);
var dst = path.join(__dirname, fileName);
fs.unlink(dst, function() {
fs.symlink(src, dst, callback);
});
};
}
if (semver.gte(process.version, "0.11.2")) {
enqueue("mocha", [
"--harmony",
"--reporter", "spec",
"--require", "./runtime",
"./test/tests.es6.js"
]);
}
enqueue(convert, [
"./test/tests.es6.js",
"./test/tests.es5.js"
]);
enqueue(convert, [
"./test/async.es6.js",
"./test/async.es5.js"
]);
enqueue(makeMochaCopyFunction("mocha.js"));
enqueue(makeMochaCopyFunction("mocha.css"));
// uglify-js does not work properly due to Node 0.11.7 bug.
// (https://github.com/joyent/node/issues/6235)
if (!semver.eq(process.version, "0.11.7")) {
try {
require.resolve("browserify"); // Throws if missing.
enqueue(bundle, [
["./runtime.js",
"./test/tests.es5.js",
"./test/async.es5.js"],
"./test/tests.browser.js"
]);
} catch (ignored) {
console.error("browserify not installed; skipping bundle step");
}
}
enqueue("mocha", [
"--reporter", "spec",
"--require", "./runtime",
"./test/tests.es5.js",
"./test/async.es5.js",
"./test/tests.transform.js"
]);
// Run command-line tool with available options to make sure it works.
enqueue("./bin/regenerator", [
"./test/async.es5.js"
], true);
enqueue("./bin/regenerator", [
"--include-runtime",
"./test/async.es5.js"
], true);
// Make sure we run the command-line tool on a file that does not need any
// transformation, too.
enqueue("./bin/regenerator", [
"./test/nothing-to-transform.js"
], true);
enqueue("./bin/regenerator", [
"--include-runtime",
"./test/nothing-to-transform.js"
], true);
flush();

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
var regenerator = require("..");
var babylon = require("babylon");
var assert = require("assert");
var babel = require("babel-core");
var t = require("babel-types");
describe("_blockHoist nodes", function() {
it("should be hoisted to the outer body", function() {
var foo;
var names = [];
var ast = babylon.parse([
"function *foo(doNotHoistMe, hoistMe) {",
" var sent = yield doNotHoistMe();",
" hoistMe();",
" names.push(sent);",
" return 123;",
"}"
].join("\n"));
var hoistMeStmt = ast.program.body[0].body.body[1];
t.assertExpressionStatement(hoistMeStmt);
t.assertCallExpression(hoistMeStmt.expression);
t.assertIdentifier(hoistMeStmt.expression.callee);
assert.strictEqual(hoistMeStmt.expression.callee.name, "hoistMe");
hoistMeStmt._blockHoist = 1;
eval(babel.transformFromAst(ast, null, { plugins: [regenerator] }).code);
assert.strictEqual(typeof foo, "function");
assert.ok(regeneratorRuntime.isGeneratorFunction(foo));
assert.strictEqual(names.length, 0);
var g = foo(function doNotHoistMe() {
names.push("doNotHoistMe");
return "yielded";
}, function hoistMe() {
names.push("hoistMe");
});
assert.deepEqual(names, ["hoistMe"]);
assert.deepEqual(g.next(), { value: "yielded", done: false });
assert.deepEqual(names, ["hoistMe", "doNotHoistMe"]);
assert.deepEqual(g.next("oyez"), { value: 123, done: true });
assert.deepEqual(names, ["hoistMe", "doNotHoistMe", "oyez"]);
});
});

View File

@ -1,11 +0,0 @@
language: node_js
node_js:
- "4.0"
- "iojs"
- "0.12"
- "0.11"
- "0.10"
- "0.8"
before_install:
- npm install -g npm@1.4.28
sudo: false

View File

@ -1,80 +0,0 @@
# Contributing to Regenerator
Regenerator uses GitHub as its sole source of truth. Everything happens
here. Facebook employees who contribute to Regenerator are expected to do
so in the same way as everyone else. In other words, this document applies
equally to all contributors.
### `master` is unsafe
We will do our best to keep `master` in good shape, with tests passing at
all times. But in order to move fast, we will make API changes that your
application might not be compatible with. We will do our best to
communicate these changes and always version appropriately so you can lock
into a specific version if need be.
### Pull Requests
In case you've never submitted a pull request (PR) via GitHub before,
please read [this short
tutorial](https://help.github.com/articles/creating-a-pull-request). If
you've submitted a PR before, there should be nothing surprising about our
procedures for Regenerator.
*Before* submitting a pull request, please make sure the following is done…
1. Fork the repo and create your branch from `master`.
2. If you've added code that should be tested, add tests!
3. Ensure the test suite passes (`npm test`).
4. If you haven't already, complete the CLA.
5. Submit a pull request via GitHub.
6. Check that Travis CI tests pass (pull request turns green).
### Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You
only need to do this once, so if you've done this for another Facebook
open source project, you're good to go. If you are submitting a pull
request for the first time, just let us know that you have completed the
CLA and we can cross-check with your GitHub username.
Complete your CLA here: <https://code.facebook.com/cla>
## Bugs
### Where to Find Known Issues
We will be using GitHub Issues for all bugs. Before filing a new issue,
please try to make sure your problem doesn't already exist. If you think
your issue is more general than one that already exists, our preference is
still to modify the original issue to reflect the underlying problem more
faithfully.
### Reporting New Issues
The best way to get a bug fixed is to provide a reduced test case, and the
easiest way to reduce a testcase is to edit it in [the
sandbox](http://facebook.github.io/regenerator/) until you're satisfied
and then click the "report a bug" link (the new issue will be populated
automatically with your code).
### Security Bugs
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for
the safe disclosure of security bugs. With that in mind, please do not
file public issues and go through the process outlined on that page.
## Coding Style
* Use semicolons;
* Commas last,
* 2 spaces for indentation (no tabs).
* Prefer `"` over `'`
* 80 character line length
* Match surrounding coding style.
* Less code is better code.
## License
By contributing to Regenerator, you agree that your contributions will be
licensed under the [BSD License](LICENSE).

View File

@ -1,74 +1 @@
regenerator [![Build Status](https://travis-ci.org/facebook/regenerator.png?branch=master)](https://travis-ci.org/facebook/regenerator)
===
This package implements a fully-functional source transformation that
takes the proposed syntax for generators/`yield` from future versions of
JS ([ECMAScript6 or ES6](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts), experimentally implemented in Node.js v0.11) and
spits out efficient JS-of-today (ES5) that behaves the same way.
A small runtime library (less than 1KB compressed) is required to provide the
`wrapGenerator` function. You can install it either as a CommonJS module
or as a standalone .js file, whichever you prefer.
Installation
---
From NPM:
```sh
npm install -g regenerator
```
From GitHub:
```sh
cd path/to/node_modules
git clone git://github.com/facebook/regenerator.git
cd regenerator
npm install .
npm test
```
Usage
---
You have several options for using this module.
Simplest usage:
```sh
regenerator es6.js > es5.js # Just the transform.
regenerator --include-runtime es6.js > es5.js # Add the runtime too.
regenerator src lib # Transform every .js file in src and output to lib.
```
Programmatic usage:
```js
var es5Source = require("regenerator").compile(es6Source).code;
var es5SourceWithRuntime = require("regenerator").compile(es6Source, {
includeRuntime: true
}).code;
```
Babel plugin:
```js
var babel = require("babel-core");
var code = babel.transform(es6Source, {
plugins: [require("generator")]
}).code;
```
How can you get involved?
---
The easiest way to get involved is to look for buggy examples using [the
sandbox](http://facebook.github.io/regenerator/), and when you find
something strange just click the "report a bug" link (the new issue form
will be populated automatically with the problematic code).
Alternatively, you can
[fork](https://github.com/facebook/regenerator/fork) the repository,
create some failing tests cases in [test/tests.es6.js](test/tests.es6.js),
and send pull requests for me to fix.
If you're feeling especially brave, you are more than welcome to dive into
the transformer code and fix the bug(s) yourself, but I must warn you that
the code could really benefit from [better implementation
comments](https://github.com/facebook/regenerator/issues/7).
# babel-plugin-transform-regenerator

View File

@ -1,17 +0,0 @@
#!/usr/bin/env node
// -*- mode: js -*-
var compile = require("../main").compile;
require("commoner").version(
require("../package.json").version
).resolve(function(id) {
return this.readModuleP(id);
}).option(
"-r, --include-runtime",
"Prepend the runtime to the output."
).process(function(id, source) {
return compile(source, {
includeRuntime: this.options.includeRuntime
}).code;
});

View File

@ -8,54 +8,66 @@
* the same directory.
*/
var traverse = require("babel-traverse");
var assert = require("assert");
var t = require("babel-types");
var leap = require("./leap");
var meta = require("./meta");
var util = require("./util");
var runtimeProperty = util.runtimeProperty;
"use strict";
var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var _assert = require("assert");
var _assert2 = _interopRequireDefault(_assert);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _leap = require("./leap");
var leap = _interopRequireWildcard(_leap);
var _meta = require("./meta");
var meta = _interopRequireWildcard(_meta);
var _util = require("./util");
var util = _interopRequireWildcard(_util);
var hasOwn = Object.prototype.hasOwnProperty;
function Emitter(contextId) {
assert.ok(this instanceof Emitter);
_assert2["default"].ok(this instanceof Emitter);
t.assertIdentifier(contextId);
// Used to generate unique temporary names.
this.nextTempId = 0;
Object.defineProperties(this, {
// In order to make sure the context object does not collide with
// anything in the local scope, we might have to rename it, so we
// refer to it symbolically instead of just assuming that it will be
// called "context".
contextId: { value: contextId },
this.contextId = contextId;
// An append-only list of Statements that grows each time this.emit is
// called.
listing: { value: [] },
this.listing = [];
// A sparse array whose keys correspond to locations in this.listing
// that have been marked as branch/jump targets.
marked: { value: [true] },
this.marked = [true];
// The last location will be marked when this.getDispatchLoop is
// called.
finalLoc: { value: loc() },
this.finalLoc = loc();
// A list of all leap.TryEntry statements emitted.
tryEntries: { value: [] }
});
this.tryEntries = [];
// The .leapManager property needs to be defined by a separate
// defineProperties call so that .finalLoc will be visible to the
// leap.LeapManager constructor.
Object.defineProperties(this, {
// Each time we evaluate the body of a loop, we tell this.leapManager
// to enter a nested loop context that determines the meaning of break
// and continue statements therein.
leapManager: { value: new leap.LeapManager(this) }
});
this.leapManager = new leap.LeapManager(this);
}
var Ep = Emitter.prototype;
@ -80,15 +92,14 @@ Ep.mark = function(loc) {
} else {
// Locations can be marked redundantly, but their values cannot change
// once set the first time.
assert.strictEqual(loc.value, index);
_assert2["default"].strictEqual(loc.value, index);
}
this.marked[index] = true;
return loc;
};
Ep.emit = function (node) {
if (t.isExpression(node))
node = t.expressionStatement(node);
if (t.isExpression(node)) node = t.expressionStatement(node);
t.assertStatement(node);
this.listing.push(node);
};
@ -102,18 +113,13 @@ Ep.emitAssign = function(lhs, rhs) {
// Shorthand for an assignment statement.
Ep.assign = function (lhs, rhs) {
return t.expressionStatement(
t.assignmentExpression("=", lhs, rhs));
return t.expressionStatement(t.assignmentExpression("=", lhs, rhs));
};
// Convenience function for generating expressions like context.next,
// context.sent, and context.rval.
Ep.contextProperty = function (name, computed) {
return t.memberExpression(
this.contextId,
computed ? t.stringLiteral(name) : t.identifier(name),
!!computed
);
return t.memberExpression(this.contextId, computed ? t.stringLiteral(name) : t.identifier(name), !!computed);
};
// Shorthand for setting context.rval and jumping to `context.stop()`.
@ -128,19 +134,13 @@ Ep.stop = function(rval) {
Ep.setReturnValue = function (valuePath) {
t.assertExpression(valuePath.value);
this.emitAssign(
this.contextProperty("rval"),
this.explodeExpression(valuePath)
);
this.emitAssign(this.contextProperty("rval"), this.explodeExpression(valuePath));
};
Ep.clearPendingException = function (tryLoc, assignee) {
t.assertLiteral(tryLoc);
var catchCall = t.callExpression(
this.contextProperty("catch", true),
[tryLoc]
);
var catchCall = t.callExpression(this.contextProperty("catch", true), [tryLoc]);
if (assignee) {
this.emitAssign(assignee, catchCall);
@ -161,13 +161,7 @@ Ep.jumpIf = function(test, toLoc) {
t.assertExpression(test);
t.assertLiteral(toLoc);
this.emit(t.ifStatement(
test,
t.blockStatement([
this.assign(this.contextProperty("next"), toLoc),
t.breakStatement()
])
));
this.emit(t.ifStatement(test, t.blockStatement([this.assign(this.contextProperty("next"), toLoc), t.breakStatement()])));
};
// Conditional jump, with the condition negated.
@ -175,22 +169,15 @@ Ep.jumpIfNot = function(test, toLoc) {
t.assertExpression(test);
t.assertLiteral(toLoc);
var negatedTest;
if (t.isUnaryExpression(test) &&
test.operator === "!") {
var negatedTest = undefined;
if (t.isUnaryExpression(test) && test.operator === "!") {
// Avoid double negation.
negatedTest = test.argument;
} else {
negatedTest = t.unaryExpression("!", test);
}
this.emit(t.ifStatement(
negatedTest,
t.blockStatement([
this.assign(this.contextProperty("next"), toLoc),
t.breakStatement()
])
));
this.emit(t.ifStatement(negatedTest, t.blockStatement([this.assign(this.contextProperty("next"), toLoc), t.breakStatement()])));
};
// Returns a unique MemberExpression that can be used to store and
@ -203,11 +190,8 @@ Ep.makeTempVar = function() {
};
Ep.getContextFunction = function (id) {
return t.functionExpression(
id || null/*Anonymous*/,
[this.contextId],
t.blockStatement([this.getDispatchLoop()]),
false, // Not a generator anymore!
return t.functionExpression(id || null, /*Anonymous*/
[this.contextId], t.blockStatement([this.getDispatchLoop()]), false, // Not a generator anymore!
false // Nor an expression.
);
};
@ -226,7 +210,7 @@ Ep.getContextFunction = function(id) {
Ep.getDispatchLoop = function () {
var self = this;
var cases = [];
var current;
var current = undefined;
// If we encounter a break, continue, or return statement in a switch
// case, we can skip the rest of the statements until the next case.
@ -234,16 +218,13 @@ Ep.getDispatchLoop = function() {
self.listing.forEach(function (stmt, i) {
if (self.marked.hasOwnProperty(i)) {
cases.push(t.switchCase(
t.numericLiteral(i),
current = []));
cases.push(t.switchCase(t.numericLiteral(i), current = []));
alreadyEnded = false;
}
if (!alreadyEnded) {
current.push(stmt);
if (t.isCompletionStatement(stmt))
alreadyEnded = true;
if (t.isCompletionStatement(stmt)) alreadyEnded = true;
}
});
@ -251,8 +232,7 @@ Ep.getDispatchLoop = function() {
// we can finally resolve this.finalLoc.value.
this.finalLoc.value = this.listing.length;
cases.push(
t.switchCase(this.finalLoc, [
cases.push(t.switchCase(this.finalLoc, [
// Intentionally fall through to the "end" case...
]),
@ -260,23 +240,9 @@ Ep.getDispatchLoop = function() {
// to know its offset, we provide the "end" case as a synonym.
t.switchCase(t.stringLiteral("end"), [
// This will check/clear both context.thrown and context.rval.
t.returnStatement(
t.callExpression(this.contextProperty("stop"), [])
)
])
);
t.returnStatement(t.callExpression(this.contextProperty("stop"), []))]));
return t.whileStatement(
t.numericLiteral(1),
t.switchStatement(
t.assignmentExpression(
"=",
this.contextProperty("prev"),
this.contextProperty("next")
),
cases
)
);
return t.whileStatement(t.numericLiteral(1), t.switchStatement(t.assignmentExpression("=", this.contextProperty("prev"), this.contextProperty("next")), cases));
};
Ep.getTryLocsList = function () {
@ -288,20 +254,17 @@ Ep.getTryLocsList = function() {
var lastLocValue = 0;
return t.arrayExpression(
this.tryEntries.map(function(tryEntry) {
return t.arrayExpression(this.tryEntries.map(function (tryEntry) {
var thisLocValue = tryEntry.firstLoc.value;
assert.ok(thisLocValue >= lastLocValue, "try entries out of order");
_assert2["default"].ok(thisLocValue >= lastLocValue, "try entries out of order");
lastLocValue = thisLocValue;
var ce = tryEntry.catchEntry;
var fe = tryEntry.finallyEntry;
var locs = [
tryEntry.firstLoc,
var locs = [tryEntry.firstLoc,
// The null here makes a hole in the array.
ce ? ce.firstLoc : null
];
ce ? ce.firstLoc : null];
if (fe) {
locs[2] = fe.firstLoc;
@ -309,8 +272,7 @@ Ep.getTryLocsList = function() {
}
return t.arrayExpression(locs);
})
);
}));
};
// All side effects must be realized in order.
@ -326,21 +288,15 @@ Ep.explode = function(path, ignoreResult) {
t.assertNode(node);
if (t.isDeclaration(node))
throw getDeclError(node);
if (t.isDeclaration(node)) throw getDeclError(node);
if (t.isStatement(node))
return self.explodeStatement(path);
if (t.isStatement(node)) return self.explodeStatement(path);
if (t.isExpression(node))
return self.explodeExpression(path, ignoreResult);
if (t.isExpression(node)) return self.explodeExpression(path, ignoreResult);
switch (node.type) {
case "Program":
return path.get("body").map(
self.explodeStatement,
self
);
return path.get("body").map(self.explodeStatement, self);
case "VariableDeclarator":
throw getDeclError(node);
@ -350,26 +306,23 @@ Ep.explode = function(path, ignoreResult) {
case "Property":
case "SwitchCase":
case "CatchClause":
throw new Error(
node.type + " nodes should be handled by their parents");
throw new Error(node.type + " nodes should be handled by their parents");
default:
throw new Error(
"unknown Node of type " +
JSON.stringify(node.type));
throw new Error("unknown Node of type " + JSON.stringify(node.type));
}
};
function getDeclError(node) {
return new Error(
"all declarations should have been transformed into " +
"assignments before the Exploder began its work: " +
JSON.stringify(node));
return new Error("all declarations should have been transformed into " + "assignments before the Exploder began its work: " + JSON.stringify(node));
}
Ep.explodeStatement = function (path, labelId) {
var stmt = path.node;
var self = this;
var before = undefined,
after = undefined,
head = undefined;
t.assertStatement(stmt);
@ -404,7 +357,7 @@ Ep.explodeStatement = function(path, labelId) {
break;
case "LabeledStatement":
var after = loc();
after = loc();
// Did you know you can break from any labeled block statement or
// control structure? Well, you can! Note: when a labeled loop is
@ -426,27 +379,23 @@ Ep.explodeStatement = function(path, labelId) {
// themselves. Also remember that labels and break/continue-to-label
// statements are rare, and all of this logic happens at transform
// time, so it has no additional runtime cost.
self.leapManager.withEntry(
new leap.LabeledEntry(after, stmt.label),
function() {
self.leapManager.withEntry(new leap.LabeledEntry(after, stmt.label), function () {
self.explodeStatement(path.get("body"), stmt.label);
}
);
});
self.mark(after);
break;
case "WhileStatement":
var before = loc();
var after = loc();
before = loc();
after = loc();
self.mark(before);
self.jumpIfNot(self.explodeExpression(path.get("test")), after);
self.leapManager.withEntry(
new leap.LoopEntry(after, before, labelId),
function() { self.explodeStatement(path.get("body")); }
);
self.leapManager.withEntry(new leap.LoopEntry(after, before, labelId), function () {
self.explodeStatement(path.get("body"));
});
self.jump(before);
self.mark(after);
@ -455,13 +404,12 @@ Ep.explodeStatement = function(path, labelId) {
case "DoWhileStatement":
var first = loc();
var test = loc();
var after = loc();
after = loc();
self.mark(first);
self.leapManager.withEntry(
new leap.LoopEntry(after, test, labelId),
function() { self.explode(path.get("body")); }
);
self.leapManager.withEntry(new leap.LoopEntry(after, test, labelId), function () {
self.explode(path.get("body"));
});
self.mark(test);
self.jumpIf(self.explodeExpression(path.get("test")), first);
self.mark(after);
@ -469,9 +417,9 @@ Ep.explodeStatement = function(path, labelId) {
break;
case "ForStatement":
var head = loc();
head = loc();
var update = loc();
var after = loc();
after = loc();
if (stmt.init) {
// We pass true here to indicate that if stmt.init is an expression
@ -487,10 +435,9 @@ Ep.explodeStatement = function(path, labelId) {
// No test means continue unconditionally.
}
self.leapManager.withEntry(
new leap.LoopEntry(after, update, labelId),
function() { self.explodeStatement(path.get("body")); }
);
self.leapManager.withEntry(new leap.LoopEntry(after, update, labelId), function () {
self.explodeStatement(path.get("body"));
});
self.mark(update);
@ -510,47 +457,22 @@ Ep.explodeStatement = function(path, labelId) {
return self.explodeExpression(path.get("expression"));
case "ForInStatement":
var head = loc();
var after = loc();
head = loc();
after = loc();
var keyIterNextFn = self.makeTempVar();
self.emitAssign(
keyIterNextFn,
t.callExpression(
runtimeProperty("keys"),
[self.explodeExpression(path.get("right"))]
)
);
self.emitAssign(keyIterNextFn, t.callExpression(util.runtimeProperty("keys"), [self.explodeExpression(path.get("right"))]));
self.mark(head);
var keyInfoTmpVar = self.makeTempVar();
self.jumpIf(
t.memberExpression(
t.assignmentExpression(
"=",
keyInfoTmpVar,
t.callExpression(keyIterNextFn, [])
),
t.identifier("done"),
false
),
after
);
self.jumpIf(t.memberExpression(t.assignmentExpression("=", keyInfoTmpVar, t.callExpression(keyIterNextFn, [])), t.identifier("done"), false), after);
self.emitAssign(
stmt.left,
t.memberExpression(
keyInfoTmpVar,
t.identifier("value"),
false
)
);
self.emitAssign(stmt.left, t.memberExpression(keyInfoTmpVar, t.identifier("value"), false));
self.leapManager.withEntry(
new leap.LoopEntry(after, head, labelId),
function() { self.explodeStatement(path.get("body")); }
);
self.leapManager.withEntry(new leap.LoopEntry(after, head, labelId), function () {
self.explodeStatement(path.get("body"));
});
self.jump(head);
@ -577,12 +499,9 @@ Ep.explodeStatement = function(path, labelId) {
case "SwitchStatement":
// Always save the discriminant into a temporary variable in case the
// test expressions overwrite values like context.sent.
var disc = self.emitAssign(
self.makeTempVar(),
self.explodeExpression(path.get("discriminant"))
);
var disc = self.emitAssign(self.makeTempVar(), self.explodeExpression(path.get("discriminant")));
var after = loc();
after = loc();
var defaultLoc = loc();
var condition = defaultLoc;
var caseLocs = [];
@ -595,11 +514,7 @@ Ep.explodeStatement = function(path, labelId) {
t.assertSwitchCase(c);
if (c.test) {
condition = t.conditionalExpression(
t.binaryExpression("===", disc, c.test),
caseLocs[i] = loc(),
condition
);
condition = t.conditionalExpression(t.binaryExpression("===", disc, c.test), caseLocs[i] = loc(), condition);
} else {
caseLocs[i] = defaultLoc;
}
@ -609,38 +524,30 @@ Ep.explodeStatement = function(path, labelId) {
discriminant.replaceWith(condition);
self.jump(self.explodeExpression(discriminant));
self.leapManager.withEntry(
new leap.SwitchEntry(after),
function() {
self.leapManager.withEntry(new leap.SwitchEntry(after), function () {
path.get("cases").forEach(function (casePath) {
var c = casePath.node;
var i = casePath.key;
self.mark(caseLocs[i]);
casePath.get("consequent").forEach(function (path) {
self.explodeStatement(path);
});
});
}
);
});
self.mark(after);
if (defaultLoc.value === -1) {
self.mark(defaultLoc);
assert.strictEqual(after.value, defaultLoc.value);
_assert2["default"].strictEqual(after.value, defaultLoc.value);
}
break;
case "IfStatement":
var elseLoc = stmt.alternate && loc();
var after = loc();
after = loc();
self.jumpIfNot(
self.explodeExpression(path.get("test")),
elseLoc || after
);
self.jumpIfNot(self.explodeExpression(path.get("test")), elseLoc || after);
self.explodeStatement(path.get("consequent"));
@ -663,29 +570,20 @@ Ep.explodeStatement = function(path, labelId) {
break;
case "WithStatement":
throw new Error(
node.type + " not supported in generator functions.");
throw new Error("WithStatement not supported in generator functions.");
case "TryStatement":
var after = loc();
after = loc();
var handler = stmt.handler;
var catchLoc = handler && loc();
var catchEntry = catchLoc && new leap.CatchEntry(
catchLoc,
handler.param
);
var catchEntry = catchLoc && new leap.CatchEntry(catchLoc, handler.param);
var finallyLoc = stmt.finalizer && loc();
var finallyEntry = finallyLoc &&
new leap.FinallyEntry(finallyLoc, after);
var finallyEntry = finallyLoc && new leap.FinallyEntry(finallyLoc, after);
var tryEntry = new leap.TryEntry(
self.getUnmarkedCurrentLoc(),
catchEntry,
finallyEntry
);
var tryEntry = new leap.TryEntry(self.getUnmarkedCurrentLoc(), catchEntry, finallyEntry);
self.tryEntries.push(tryEntry);
self.updateContextPrevLoc(tryEntry.firstLoc);
@ -694,12 +592,12 @@ Ep.explodeStatement = function(path, labelId) {
self.explodeStatement(path.get("block"));
if (catchLoc) {
(function () {
if (finallyLoc) {
// If we have both a catch block and a finally block, then
// because we emit the catch block first, we need to jump over
// it to the finally block.
self.jump(finallyLoc);
} else {
// If there is no finally block, then we need to jump over the
// catch block to the fall-through location.
@ -712,10 +610,6 @@ Ep.explodeStatement = function(path, labelId) {
var safeParam = self.makeTempVar();
self.clearPendingException(tryEntry.firstLoc, safeParam);
var catchScope = bodyPath.scope;
// TODO: t.assertCatchClause(catchScope.block);
// TODO: assert.strictEqual(catchScope.lookup(catchParamName), catchScope);
bodyPath.traverse(catchParamVisitor, {
safeParam: safeParam,
catchParamName: handler.param.name
@ -724,6 +618,7 @@ Ep.explodeStatement = function(path, labelId) {
self.leapManager.withEntry(catchEntry, function () {
self.explodeStatement(bodyPath);
});
})();
}
if (finallyLoc) {
@ -733,10 +628,7 @@ Ep.explodeStatement = function(path, labelId) {
self.explodeStatement(path.get("finalizer"));
});
self.emit(t.returnStatement(t.callExpression(
self.contextProperty("finish"),
[finallyEntry.firstLoc]
)));
self.emit(t.returnStatement(t.callExpression(self.contextProperty("finish"), [finallyEntry.firstLoc])));
}
});
@ -745,27 +637,23 @@ Ep.explodeStatement = function(path, labelId) {
break;
case "ThrowStatement":
self.emit(t.throwStatement(
self.explodeExpression(path.get("argument"))
));
self.emit(t.throwStatement(self.explodeExpression(path.get("argument"))));
break;
default:
throw new Error(
"unknown Statement of type " +
JSON.stringify(stmt.type));
throw new Error("unknown Statement of type " + JSON.stringify(stmt.type));
}
};
var catchParamVisitor = {
Identifier: function(path, state) {
Identifier: function Identifier(path, state) {
if (path.node.name === state.catchParamName && util.isReference(path)) {
path.replaceWith(state.safeParam);
}
},
Scope: function(path, state) {
Scope: function Scope(path, state) {
if (path.scope.hasOwnBinding(state.catchParamName)) {
// Don't descend into nested scopes that shadow the catch
// parameter with their own declarations.
@ -776,40 +664,24 @@ var catchParamVisitor = {
Ep.emitAbruptCompletion = function (record) {
if (!isValidCompletion(record)) {
assert.ok(
false,
"invalid completion record: " +
JSON.stringify(record)
);
_assert2["default"].ok(false, "invalid completion record: " + JSON.stringify(record));
}
assert.notStrictEqual(
record.type, "normal",
"normal completions are not abrupt"
);
_assert2["default"].notStrictEqual(record.type, "normal", "normal completions are not abrupt");
var abruptArgs = [t.stringLiteral(record.type)];
if (record.type === "break" ||
record.type === "continue") {
if (record.type === "break" || record.type === "continue") {
t.assertLiteral(record.target);
abruptArgs[1] = record.target;
} else if (record.type === "return" ||
record.type === "throw") {
} else if (record.type === "return" || record.type === "throw") {
if (record.value) {
t.assertExpression(record.value);
abruptArgs[1] = record.value;
}
}
this.emit(
t.returnStatement(
t.callExpression(
this.contextProperty("abrupt"),
abruptArgs
)
)
);
this.emit(t.returnStatement(t.callExpression(this.contextProperty("abrupt"), abruptArgs)));
};
function isValidCompletion(record) {
@ -819,22 +691,17 @@ function isValidCompletion(record) {
return !hasOwn.call(record, "target");
}
if (type === "break" ||
type === "continue") {
return !hasOwn.call(record, "value")
&& t.isLiteral(record.target);
if (type === "break" || type === "continue") {
return !hasOwn.call(record, "value") && t.isLiteral(record.target);
}
if (type === "return" ||
type === "throw") {
return hasOwn.call(record, "value")
&& !hasOwn.call(record, "target");
if (type === "return" || type === "throw") {
return hasOwn.call(record, "value") && !hasOwn.call(record, "target");
}
return false;
}
// Not all offsets into emitter.listing are potential jump targets. For
// example, execution typically falls into the beginning of a try block
// without jumping directly there. This method returns the current offset
@ -868,9 +735,8 @@ Ep.updateContextPrevLoc = function(loc) {
loc.value = this.listing.length;
} else {
// Otherwise assert that the location matches the current offset.
assert.strictEqual(loc.value, this.listing.length);
_assert2["default"].strictEqual(loc.value, this.listing.length);
}
} else {
loc = this.getUnmarkedCurrentLoc();
}
@ -890,7 +756,7 @@ Ep.explodeExpression = function(path, ignoreResult) {
}
var self = this;
var result; // Used optionally by several cases below.
var result = undefined; // Used optionally by several cases below.
function finish(expr) {
t.assertExpression(expr);
@ -923,19 +789,14 @@ Ep.explodeExpression = function(path, ignoreResult) {
// control the precise order in which the generated code realizes the
// side effects of those subexpressions.
function explodeViaTempVar(tempVar, childPath, ignoreChildResult) {
assert.ok(
!ignoreChildResult || !tempVar,
"Ignoring the result of a child expression but forcing it to " +
"be assigned to a temporary variable?"
);
_assert2["default"].ok(!ignoreChildResult || !tempVar, "Ignoring the result of a child expression but forcing it to " + "be assigned to a temporary variable?");
var result = self.explodeExpression(childPath, ignoreChildResult);
if (ignoreChildResult) {
// Side effects already emitted above.
} else if (tempVar || (hasLeapingChildren &&
!t.isLiteral(result))) {
} else if (tempVar || hasLeapingChildren && !t.isLiteral(result)) {
// If tempVar was provided, then the result will always be assigned
// to it, even if the result does not otherwise need to be assigned
// to a temporary variable. When no tempVar is provided, we have
@ -947,10 +808,7 @@ Ep.explodeExpression = function(path, ignoreResult) {
// yield (see #206). One narrow case where we can prove it doesn't
// matter (and thus we do not need a temporary variable) is when the
// result in question is a Literal value.
result = self.emitAssign(
tempVar || self.makeTempVar(),
result
);
result = self.emitAssign(tempVar || self.makeTempVar(), result);
}
return result;
}
@ -961,25 +819,18 @@ Ep.explodeExpression = function(path, ignoreResult) {
switch (expr.type) {
case "MemberExpression":
return finish(t.memberExpression(
self.explodeExpression(path.get("object")),
expr.computed
? explodeViaTempVar(null, path.get("property"))
: expr.property,
expr.computed
));
return finish(t.memberExpression(self.explodeExpression(path.get("object")), expr.computed ? explodeViaTempVar(null, path.get("property")) : expr.property, expr.computed));
case "CallExpression":
var calleePath = path.get("callee");
var argsPath = path.get("arguments");
var newCallee;
var newCallee = undefined;
var newArgs = [];
var hasLeapingArgs = false;
argsPath.forEach(function (argPath) {
hasLeapingArgs = hasLeapingArgs ||
meta.containsLeap(argPath.node);
hasLeapingArgs = hasLeapingArgs || meta.containsLeap(argPath.node);
});
if (t.isMemberExpression(calleePath.node)) {
@ -993,30 +844,16 @@ Ep.explodeExpression = function(path, ignoreResult) {
var newObject = explodeViaTempVar(
// Assign the exploded callee.object expression to a temporary
// variable so that we can use it twice without reevaluating it.
self.makeTempVar(),
calleePath.get("object")
);
self.makeTempVar(), calleePath.get("object"));
var newProperty = calleePath.node.computed
? explodeViaTempVar(null, calleePath.get("property"))
: calleePath.node.property;
var newProperty = calleePath.node.computed ? explodeViaTempVar(null, calleePath.get("property")) : calleePath.node.property;
newArgs.unshift(newObject);
newCallee = t.memberExpression(
t.memberExpression(
newObject,
newProperty,
calleePath.node.computed
),
t.identifier("call"),
false
);
newCallee = t.memberExpression(t.memberExpression(newObject, newProperty, calleePath.node.computed), t.identifier("call"), false);
} else {
newCallee = self.explodeExpression(calleePath);
}
} else {
newCallee = self.explodeExpression(calleePath);
@ -1029,10 +866,7 @@ Ep.explodeExpression = function(path, ignoreResult) {
// by using the (0, object.property)(...) trick; otherwise, it
// will receive the object of the MemberExpression as its `this`
// object.
newCallee = t.sequenceExpression([
t.numericLiteral(0),
newCallee
]);
newCallee = t.sequenceExpression([t.numericLiteral(0), newCallee]);
}
}
@ -1040,40 +874,26 @@ Ep.explodeExpression = function(path, ignoreResult) {
newArgs.push(explodeViaTempVar(null, argPath));
});
return finish(t.callExpression(
newCallee,
newArgs
));
return finish(t.callExpression(newCallee, newArgs));
case "NewExpression":
return finish(t.newExpression(
explodeViaTempVar(null, path.get("callee")),
path.get("arguments").map(function(argPath) {
return finish(t.newExpression(explodeViaTempVar(null, path.get("callee")), path.get("arguments").map(function (argPath) {
return explodeViaTempVar(null, argPath);
})
));
})));
case "ObjectExpression":
return finish(t.objectExpression(
path.get("properties").map(function(propPath) {
return finish(t.objectExpression(path.get("properties").map(function (propPath) {
if (propPath.isObjectProperty()) {
return t.objectProperty(
propPath.node.key,
explodeViaTempVar(null, propPath.get("value")),
propPath.node.computed
);
return t.objectProperty(propPath.node.key, explodeViaTempVar(null, propPath.get("value")), propPath.node.computed);
} else {
return propPath.node;
}
})
));
})));
case "ArrayExpression":
return finish(t.arrayExpression(
path.get("elements").map(function(elemPath) {
return finish(t.arrayExpression(path.get("elements").map(function (elemPath) {
return explodeViaTempVar(null, elemPath);
})
));
})));
case "SequenceExpression":
var lastIndex = expr.expressions.length - 1;
@ -1089,7 +909,7 @@ Ep.explodeExpression = function(path, ignoreResult) {
return result;
case "LogicalExpression":
var after = loc();
after = loc();
if (!ignoreResult) {
result = self.makeTempVar();
@ -1100,7 +920,7 @@ Ep.explodeExpression = function(path, ignoreResult) {
if (expr.operator === "&&") {
self.jumpIfNot(left, after);
} else {
assert.strictEqual(expr.operator, "||");
_assert2["default"].strictEqual(expr.operator, "||");
self.jumpIf(left, after);
}
@ -1112,7 +932,7 @@ Ep.explodeExpression = function(path, ignoreResult) {
case "ConditionalExpression":
var elseLoc = loc();
var after = loc();
after = loc();
var test = self.explodeExpression(path.get("test"));
self.jumpIfNot(test, elseLoc);
@ -1132,53 +952,32 @@ Ep.explodeExpression = function(path, ignoreResult) {
return result;
case "UnaryExpression":
return finish(t.unaryExpression(
expr.operator,
return finish(t.unaryExpression(expr.operator,
// Can't (and don't need to) break up the syntax of the argument.
// Think about delete a[b].
self.explodeExpression(path.get("argument")),
!!expr.prefix
));
self.explodeExpression(path.get("argument")), !!expr.prefix));
case "BinaryExpression":
return finish(t.binaryExpression(
expr.operator,
explodeViaTempVar(null, path.get("left")),
explodeViaTempVar(null, path.get("right"))
));
return finish(t.binaryExpression(expr.operator, explodeViaTempVar(null, path.get("left")), explodeViaTempVar(null, path.get("right"))));
case "AssignmentExpression":
return finish(t.assignmentExpression(
expr.operator,
self.explodeExpression(path.get("left")),
self.explodeExpression(path.get("right"))
));
return finish(t.assignmentExpression(expr.operator, self.explodeExpression(path.get("left")), self.explodeExpression(path.get("right"))));
case "UpdateExpression":
return finish(t.updateExpression(
expr.operator,
self.explodeExpression(path.get("argument")),
expr.prefix
));
return finish(t.updateExpression(expr.operator, self.explodeExpression(path.get("argument")), expr.prefix));
case "YieldExpression":
var after = loc();
after = loc();
var arg = expr.argument && self.explodeExpression(path.get("argument"));
if (arg && expr.delegate) {
var result = self.makeTempVar();
var _result = self.makeTempVar();
self.emit(t.returnStatement(t.callExpression(
self.contextProperty("delegateYield"), [
arg,
t.stringLiteral(result.property.name),
after
]
)));
self.emit(t.returnStatement(t.callExpression(self.contextProperty("delegateYield"), [arg, t.stringLiteral(_result.property.name), after])));
self.mark(after);
return result;
return _result;
}
self.emitAssign(self.contextProperty("next"), after);
@ -1188,8 +987,6 @@ Ep.explodeExpression = function(path, ignoreResult) {
return self.contextProperty("sent");
default:
throw new Error(
"unknown Expression of type " +
JSON.stringify(expr.type));
throw new Error("unknown Expression of type " + JSON.stringify(expr.type));
}
};

View File

@ -8,9 +8,16 @@
* the same directory.
*/
var traverse = require("babel-traverse");
var assert = require("assert");
var t = require("babel-types");
"use strict";
var _Object$keys = require("babel-runtime/core-js/object/keys")["default"];
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var hasOwn = Object.prototype.hasOwnProperty;
// The hoist function takes a FunctionExpression or FunctionDeclaration
@ -31,26 +38,22 @@ exports.hoist = function(funPath) {
vars[dec.id.name] = dec.id;
if (dec.init) {
exprs.push(t.assignmentExpression(
"=", dec.id, 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 === 0) return null;
if (exprs.length === 1)
return exprs[0];
if (exprs.length === 1) return exprs[0];
return t.sequenceExpression(exprs);
}
funPath.get("body").traverse({
VariableDeclaration: {
exit: function(path) {
exit: function exit(path) {
var expr = varDeclToExpr(path.node, false);
if (expr === null) {
path.remove();
@ -66,38 +69,25 @@ exports.hoist = function(funPath) {
}
},
ForStatement: function(path) {
ForStatement: function ForStatement(path) {
var init = path.node.init;
if (t.isVariableDeclaration(init)) {
path.get("init").replaceWith(varDeclToExpr(init, false));
}
},
ForXStatement: function(path) {
ForXStatement: function ForXStatement(path) {
var left = path.get("left");
if (left.isVariableDeclaration()) {
left.replaceWith(varDeclToExpr(left.node, true));
}
},
FunctionDeclaration: function(path) {
FunctionDeclaration: function FunctionDeclaration(path) {
var node = path.node;
vars[node.id.name] = node.id;
var parentNode = path.parent.node;
var assignment = t.expressionStatement(
t.assignmentExpression(
"=",
node.id,
t.functionExpression(
node.id,
node.params,
node.body,
node.generator,
node.expression
)
)
);
var 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
@ -118,7 +108,7 @@ exports.hoist = function(funPath) {
path.skip();
},
FunctionExpression: function(path) {
FunctionExpression: function FunctionExpression(path) {
// Don't descend into nested function expressions.
path.skip();
}
@ -137,7 +127,7 @@ exports.hoist = function(funPath) {
var declarations = [];
Object.keys(vars).forEach(function(name) {
_Object$keys(vars).forEach(function (name) {
if (!hasOwn.call(paramNames, name)) {
declarations.push(t.variableDeclarator(vars[name], null));
}

View File

@ -8,13 +8,24 @@
* the same directory.
*/
var assert = require("assert");
var t = require("babel-types");
var inherits = require("util").inherits;
var hasOwn = Object.prototype.hasOwnProperty;
"use strict";
var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var _assert = require("assert");
var _assert2 = _interopRequireDefault(_assert);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _util = require("util");
function Entry() {
assert.ok(this instanceof Entry);
_assert2["default"].ok(this instanceof Entry);
}
function FunctionEntry(returnLoc) {
@ -23,7 +34,7 @@ function FunctionEntry(returnLoc) {
this.returnLoc = returnLoc;
}
inherits(FunctionEntry, Entry);
_util.inherits(FunctionEntry, Entry);
exports.FunctionEntry = FunctionEntry;
function LoopEntry(breakLoc, continueLoc, label) {
@ -43,7 +54,7 @@ function LoopEntry(breakLoc, continueLoc, label) {
this.label = label;
}
inherits(LoopEntry, Entry);
_util.inherits(LoopEntry, Entry);
exports.LoopEntry = LoopEntry;
function SwitchEntry(breakLoc) {
@ -52,7 +63,7 @@ function SwitchEntry(breakLoc) {
this.breakLoc = breakLoc;
}
inherits(SwitchEntry, Entry);
_util.inherits(SwitchEntry, Entry);
exports.SwitchEntry = SwitchEntry;
function TryEntry(firstLoc, catchEntry, finallyEntry) {
@ -61,26 +72,26 @@ function TryEntry(firstLoc, catchEntry, finallyEntry) {
t.assertLiteral(firstLoc);
if (catchEntry) {
assert.ok(catchEntry instanceof CatchEntry);
_assert2["default"].ok(catchEntry instanceof CatchEntry);
} else {
catchEntry = null;
}
if (finallyEntry) {
assert.ok(finallyEntry instanceof FinallyEntry);
_assert2["default"].ok(finallyEntry instanceof FinallyEntry);
} else {
finallyEntry = null;
}
// Have to have one or the other (or both).
assert.ok(catchEntry || finallyEntry);
_assert2["default"].ok(catchEntry || finallyEntry);
this.firstLoc = firstLoc;
this.catchEntry = catchEntry;
this.finallyEntry = finallyEntry;
}
inherits(TryEntry, Entry);
_util.inherits(TryEntry, Entry);
exports.TryEntry = TryEntry;
function CatchEntry(firstLoc, paramId) {
@ -93,7 +104,7 @@ function CatchEntry(firstLoc, paramId) {
this.paramId = paramId;
}
inherits(CatchEntry, Entry);
_util.inherits(CatchEntry, Entry);
exports.CatchEntry = CatchEntry;
function FinallyEntry(firstLoc, afterLoc) {
@ -104,7 +115,7 @@ function FinallyEntry(firstLoc, afterLoc) {
this.afterLoc = afterLoc;
}
inherits(FinallyEntry, Entry);
_util.inherits(FinallyEntry, Entry);
exports.FinallyEntry = FinallyEntry;
function LabeledEntry(breakLoc, label) {
@ -117,14 +128,14 @@ function LabeledEntry(breakLoc, label) {
this.label = label;
}
inherits(LabeledEntry, Entry);
_util.inherits(LabeledEntry, Entry);
exports.LabeledEntry = LabeledEntry;
function LeapManager(emitter) {
assert.ok(this instanceof LeapManager);
_assert2["default"].ok(this instanceof LeapManager);
var Emitter = require("./emit").Emitter;
assert.ok(emitter instanceof Emitter);
_assert2["default"].ok(emitter instanceof Emitter);
this.emitter = emitter;
this.entryStack = [new FunctionEntry(emitter.finalLoc)];
@ -134,13 +145,13 @@ var LMp = LeapManager.prototype;
exports.LeapManager = LeapManager;
LMp.withEntry = function (entry, callback) {
assert.ok(entry instanceof Entry);
_assert2["default"].ok(entry instanceof Entry);
this.entryStack.push(entry);
try {
callback.call(this.emitter);
} finally {
var popped = this.entryStack.pop();
assert.strictEqual(popped, entry);
_assert2["default"].strictEqual(popped, entry);
}
};
@ -150,8 +161,7 @@ LMp._findLeapLocation = function(property, label) {
var loc = entry[property];
if (loc) {
if (label) {
if (entry.label &&
entry.label.name === label.name) {
if (entry.label && entry.label.name === label.name) {
return loc;
}
} else if (entry instanceof LabeledEntry) {

View File

@ -8,9 +8,22 @@
* the same directory.
*/
var assert = require("assert");
"use strict";
var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var _assert = require("assert");
var _assert2 = _interopRequireDefault(_assert);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var m = require("private").makeAccessor();
var t = require("babel-types");
var hasOwn = Object.prototype.hasOwnProperty;
function makePredicate(propertyName, knownTypes) {
@ -26,7 +39,7 @@ function makePredicate(propertyName, knownTypes) {
} else if (Array.isArray(child)) {
child.some(check);
} else if (t.isNode(child)) {
assert.strictEqual(result, false);
_assert2["default"].strictEqual(result, false);
result = predicate(child);
}
return result;
@ -48,16 +61,13 @@ function makePredicate(propertyName, knownTypes) {
t.assertNode(node);
var meta = m(node);
if (hasOwn.call(meta, propertyName))
return meta[propertyName];
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(opaqueTypes, node.type)) return meta[propertyName] = false;
if (hasOwn.call(knownTypes, node.type))
return meta[propertyName] = true;
if (hasOwn.call(knownTypes, node.type)) return meta[propertyName] = true;
return meta[propertyName] = onlyChildren(node);
}

View File

@ -8,16 +8,22 @@
* the same directory.
*/
var t = require("babel-types");
"use strict";
exports.runtimeProperty = function(name) {
return t.memberExpression(
t.identifier("regeneratorRuntime"),
t.identifier(name),
false
);
};
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
exports.isReference = function(path) {
exports.__esModule = true;
exports.runtimeProperty = runtimeProperty;
exports.isReference = isReference;
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
function runtimeProperty(name) {
return t.memberExpression(t.identifier("regeneratorRuntime"), t.identifier(name), false);
}
function isReference(path) {
return path.isReferenced() || path.parentPath.isAssignmentExpression({ left: path.node });
};
}

View File

@ -8,20 +8,33 @@
* the same directory.
*/
var traverse = require("babel-traverse");
var babylon = require("babylon");
var assert = require("assert");
var fs = require("fs");
var t = require("babel-types");
var hoist = require("./hoist").hoist;
var Emitter = require("./emit").Emitter;
var util = require("./util");
var runtimeProperty = util.runtimeProperty;
"use strict";
var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var _assert = require("assert");
var _assert2 = _interopRequireDefault(_assert);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
var _hoist = require("./hoist");
var _emit = require("./emit");
var _util = require("./util");
var util = _interopRequireWildcard(_util);
var getMarkInfo = require("private").makeAccessor();
exports.visitor = {
Function: {
exit: function(path, state) {
exit: function exit(path, state) {
var node = path.node;
if (node.generator) {
@ -43,9 +56,7 @@ exports.visitor = {
if (node.expression) {
// Transform expression lambdas into normal functions.
node.expression = false;
node.body = t.blockStatement([
t.returnStatement(node.body)
]);
node.body = t.blockStatement([t.returnStatement(node.body)]);
}
if (node.async) {
@ -82,41 +93,33 @@ exports.visitor = {
// Turn all declarations into vars, and replace the original
// declarations with equivalent assignment expressions.
var vars = hoist(path);
var vars = _hoist.hoist(path);
var didRenameArguments = renameArguments(path, argsId);
if (didRenameArguments) {
vars = vars || t.variableDeclaration("var", []);
vars.declarations.push(t.variableDeclarator(
argsId, t.identifier("arguments")
));
vars.declarations.push(t.variableDeclarator(argsId, t.identifier("arguments")));
}
var emitter = new Emitter(contextId);
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 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()
];
node.generator ? outerFnExpr : t.nullLiteral(), t.thisExpression()];
var tryLocsList = emitter.getTryLocsList();
if (tryLocsList) {
wrapArgs.push(tryLocsList);
}
var wrapCall = t.callExpression(
runtimeProperty(node.async ? "async" : "wrap"),
wrapArgs
);
var wrapCall = t.callExpression(util.runtimeProperty(node.async ? "async" : "wrap"), wrapArgs);
outerBody.push(t.returnStatement(wrapCall));
node.body = t.blockStatement(outerBody);
@ -130,9 +133,8 @@ exports.visitor = {
node.async = false;
}
if (wasGeneratorFunction &&
t.isExpression(node)) {
path.replaceWith(t.callExpression(runtimeProperty("mark"), [node]));
if (wasGeneratorFunction && t.isExpression(node)) {
path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node]));
}
}
}
@ -164,40 +166,22 @@ function getOuterFnExpr(funPath) {
var index = funDeclIdArray.elements.length;
funDeclIdArray.elements.push(node.id);
return t.memberExpression(
markedArray,
t.numericLiteral(index),
true
);
return t.memberExpression(markedArray, t.numericLiteral(index), true);
}
return node.id || (
node.id = funPath.scope.parent.generateUidIdentifier("callee")
);
return node.id || (node.id = funPath.scope.parent.generateUidIdentifier("callee"));
}
function getRuntimeMarkDecl(blockPath) {
var block = blockPath.node;
assert.ok(Array.isArray(block.body));
_assert2["default"].ok(Array.isArray(block.body));
var 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
),
[runtimeProperty("mark")]
)
)
]);
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);
@ -220,11 +204,11 @@ function renameArguments(funcPath, argsId) {
}
var argumentsVisitor = {
"FunctionExpression|FunctionDeclaration": function(path) {
"FunctionExpression|FunctionDeclaration": function FunctionExpressionFunctionDeclaration(path) {
path.skip();
},
Identifier: function(path, state) {
Identifier: function Identifier(path, state) {
if (path.node.name === "arguments" && util.isReference(path)) {
path.replaceWith(state.argsId);
state.didRenameArguments = true;
@ -233,23 +217,17 @@ var argumentsVisitor = {
};
var awaitVisitor = {
Function: function(path) {
Function: function Function(path) {
path.skip(); // Don't descend into nested function scopes.
},
AwaitExpression: function(path) {
AwaitExpression: function AwaitExpression(path) {
// Convert await and 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.
path.replaceWith(t.yieldExpression(
t.callExpression(
runtimeProperty("awrap"),
[argument]
),
false
));
path.replaceWith(t.yieldExpression(t.callExpression(util.runtimeProperty("awrap"), [argument]), false));
}
};

View File

@ -1,34 +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 asyncFunctionSyntax = require("babel-plugin-syntax-async-functions");
var blockScopingPlugin = require("babel-plugin-transform-es2015-block-scoping");
var forOfPlugin = require("babel-plugin-transform-es2015-for-of");
var babel = require("babel-core");
var regenerator = module.exports = function() {
return require("./lib/visit");
};
regenerator.compile = function(code, opts) {
// todo: includeRuntime
return babel.transform(code, buildBabelOptions(opts));
};
regenerator.transform = function (ast, opts) {
return babel.transformFromAst(ast, null, buildBabelOptions(opts));
};
function buildBabelOptions(opts) {
return {
plugins: [regenerator, blockScopingPlugin, asyncFunctionSyntax, forOfPlugin],
sourceType: "script"
};
}

View File

@ -1,32 +1,10 @@
{
"author": "Ben Newman <bn@cs.stanford.edu>",
"name": "babel-plugin-transform-regenerator",
"description": "Source transformer enabling ECMAScript 6 generator functions (yield) in JavaScript-of-today (ES5)",
"keywords": [
"generator",
"yield",
"coroutine",
"rewriting",
"transformation",
"syntax",
"codegen",
"rewriting",
"refactoring",
"transpiler",
"desugaring",
"ES6"
],
"author": "Ben Newman <bn@cs.stanford.edu>",
"description": "",
"version": "6.0.18",
"homepage": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator",
"repository": {
"type": "git",
"url": "git://github.com/facebook/regenerator.git"
},
"main": "main.js",
"bin": "bin/regenerator",
"scripts": {
"test": "node test/run.js"
},
"main": "lib/index.js",
"dependencies": {
"commoner": "~0.10.3",
"babel-plugin-transform-es2015-block-scoping": "^6.0.18",
@ -39,13 +17,5 @@
"private": "~0.1.5",
"through": "~2.3.8"
},
"devDependencies": {
"mocha": "~2.3.3",
"promise": "~7.0.4",
"semver": "~5.0.3"
},
"license": "BSD",
"engines": {
"node": ">= 0.6"
}
"license": "BSD"
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,148 @@
/**
* 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) {
vars[dec.id.name] = dec.id;
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);
};

View File

@ -0,0 +1,13 @@
/**
* 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.
*/
export default function () {
return require("./visit");
}

View File

@ -0,0 +1,174 @@
/**
* 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);
};

View File

@ -0,0 +1,103 @@
/**
* 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);

View File

@ -0,0 +1,23 @@
/**
* 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 });
}

View File

@ -0,0 +1,252 @@
/**
* 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;
}
if (node.expression) {
// Transform expression lambdas into normal functions.
node.expression = false;
node.body = t.blockStatement([
t.returnStatement(node.body)
]);
}
if (node.async) {
path.get("body").traverse(awaitVisitor);
}
let bodyBlockPath = path.get("body");
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 + "$");
let contextId = path.scope.generateUidIdentifier("context");
let argsId = path.scope.generateUidIdentifier("args");
// 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]));
}
}
}
};
// 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.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 || (
node.id = funPath.scope.parent.generateUidIdentifier("callee")
);
}
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 awaitVisitor = {
Function: function(path) {
path.skip(); // Don't descend into nested function scopes.
},
AwaitExpression: function(path) {
// Convert await and 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
));
}
};