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/helpers/*.js
/packages/babel-runtime/regenerator/*.js /packages/babel-runtime/regenerator/*.js
/packages/*/lib /packages/*/lib
!/packages/babel-plugin-transform-regenerator/lib
_babel.github.io _babel.github.io
/tests/.browser-build.js /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) # babel-plugin-transform-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).

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

View File

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

View File

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

View File

@ -8,9 +8,22 @@
* the same directory. * 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 m = require("private").makeAccessor();
var t = require("babel-types");
var hasOwn = Object.prototype.hasOwnProperty; var hasOwn = Object.prototype.hasOwnProperty;
function makePredicate(propertyName, knownTypes) { function makePredicate(propertyName, knownTypes) {
@ -26,7 +39,7 @@ function makePredicate(propertyName, knownTypes) {
} else if (Array.isArray(child)) { } else if (Array.isArray(child)) {
child.some(check); child.some(check);
} else if (t.isNode(child)) { } else if (t.isNode(child)) {
assert.strictEqual(result, false); _assert2["default"].strictEqual(result, false);
result = predicate(child); result = predicate(child);
} }
return result; return result;
@ -48,16 +61,13 @@ function makePredicate(propertyName, knownTypes) {
t.assertNode(node); t.assertNode(node);
var meta = m(node); var meta = m(node);
if (hasOwn.call(meta, propertyName)) if (hasOwn.call(meta, propertyName)) return meta[propertyName];
return meta[propertyName];
// Certain types are "opaque," which means they have no side // Certain types are "opaque," which means they have no side
// effects or leaps and we don't care about their subexpressions. // effects or leaps and we don't care about their subexpressions.
if (hasOwn.call(opaqueTypes, node.type)) if (hasOwn.call(opaqueTypes, node.type)) return meta[propertyName] = false;
return meta[propertyName] = false;
if (hasOwn.call(knownTypes, node.type)) if (hasOwn.call(knownTypes, node.type)) return meta[propertyName] = true;
return meta[propertyName] = true;
return meta[propertyName] = onlyChildren(node); return meta[propertyName] = onlyChildren(node);
} }

View File

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

View File

@ -8,20 +8,33 @@
* the same directory. * the same directory.
*/ */
var traverse = require("babel-traverse"); "use strict";
var babylon = require("babylon");
var assert = require("assert"); var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
var fs = require("fs");
var t = require("babel-types"); var _interopRequireWildcard = require("babel-runtime/helpers/interop-require-wildcard")["default"];
var hoist = require("./hoist").hoist;
var Emitter = require("./emit").Emitter; var _assert = require("assert");
var util = require("./util");
var runtimeProperty = util.runtimeProperty; 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(); var getMarkInfo = require("private").makeAccessor();
exports.visitor = { exports.visitor = {
Function: { Function: {
exit: function(path, state) { exit: function exit(path, state) {
var node = path.node; var node = path.node;
if (node.generator) { if (node.generator) {
@ -43,9 +56,7 @@ exports.visitor = {
if (node.expression) { if (node.expression) {
// Transform expression lambdas into normal functions. // Transform expression lambdas into normal functions.
node.expression = false; node.expression = false;
node.body = t.blockStatement([ node.body = t.blockStatement([t.returnStatement(node.body)]);
t.returnStatement(node.body)
]);
} }
if (node.async) { if (node.async) {
@ -82,41 +93,33 @@ exports.visitor = {
// Turn all declarations into vars, and replace the original // Turn all declarations into vars, and replace the original
// declarations with equivalent assignment expressions. // declarations with equivalent assignment expressions.
var vars = hoist(path); var vars = _hoist.hoist(path);
var didRenameArguments = renameArguments(path, argsId); var didRenameArguments = renameArguments(path, argsId);
if (didRenameArguments) { if (didRenameArguments) {
vars = vars || t.variableDeclaration("var", []); vars = vars || t.variableDeclaration("var", []);
vars.declarations.push(t.variableDeclarator( vars.declarations.push(t.variableDeclarator(argsId, t.identifier("arguments")));
argsId, t.identifier("arguments")
));
} }
var emitter = new Emitter(contextId); var emitter = new _emit.Emitter(contextId);
emitter.explode(path.get("body")); emitter.explode(path.get("body"));
if (vars && vars.declarations.length > 0) { if (vars && vars.declarations.length > 0) {
outerBody.push(vars); outerBody.push(vars);
} }
var wrapArgs = [ var wrapArgs = [emitter.getContextFunction(innerFnId),
emitter.getContextFunction(innerFnId),
// Async functions that are not generators don't care about the // 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 // outer function because they don't need it to be marked and don't
// inherit from its .prototype. // inherit from its .prototype.
node.generator ? outerFnExpr : t.nullLiteral(), node.generator ? outerFnExpr : t.nullLiteral(), t.thisExpression()];
t.thisExpression()
];
var tryLocsList = emitter.getTryLocsList(); var tryLocsList = emitter.getTryLocsList();
if (tryLocsList) { if (tryLocsList) {
wrapArgs.push(tryLocsList); wrapArgs.push(tryLocsList);
} }
var wrapCall = t.callExpression( var wrapCall = t.callExpression(util.runtimeProperty(node.async ? "async" : "wrap"), wrapArgs);
runtimeProperty(node.async ? "async" : "wrap"),
wrapArgs
);
outerBody.push(t.returnStatement(wrapCall)); outerBody.push(t.returnStatement(wrapCall));
node.body = t.blockStatement(outerBody); node.body = t.blockStatement(outerBody);
@ -130,9 +133,8 @@ exports.visitor = {
node.async = false; node.async = false;
} }
if (wasGeneratorFunction && if (wasGeneratorFunction && t.isExpression(node)) {
t.isExpression(node)) { path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node]));
path.replaceWith(t.callExpression(runtimeProperty("mark"), [node]));
} }
} }
} }
@ -164,40 +166,22 @@ function getOuterFnExpr(funPath) {
var index = funDeclIdArray.elements.length; var index = funDeclIdArray.elements.length;
funDeclIdArray.elements.push(node.id); funDeclIdArray.elements.push(node.id);
return t.memberExpression( return t.memberExpression(markedArray, t.numericLiteral(index), true);
markedArray,
t.numericLiteral(index),
true
);
} }
return node.id || ( return node.id || (node.id = funPath.scope.parent.generateUidIdentifier("callee"));
node.id = funPath.scope.parent.generateUidIdentifier("callee")
);
} }
function getRuntimeMarkDecl(blockPath) { function getRuntimeMarkDecl(blockPath) {
var block = blockPath.node; var block = blockPath.node;
assert.ok(Array.isArray(block.body)); _assert2["default"].ok(Array.isArray(block.body));
var info = getMarkInfo(block); var info = getMarkInfo(block);
if (info.decl) { if (info.decl) {
return info.decl; return info.decl;
} }
info.decl = t.variableDeclaration("var", [ info.decl = t.variableDeclaration("var", [t.variableDeclarator(blockPath.scope.generateUidIdentifier("marked"), t.callExpression(t.memberExpression(t.arrayExpression([]), t.identifier("map"), false), [util.runtimeProperty("mark")]))]);
t.variableDeclarator(
blockPath.scope.generateUidIdentifier("marked"),
t.callExpression(
t.memberExpression(
t.arrayExpression([]),
t.identifier("map"),
false
),
[runtimeProperty("mark")]
)
)
]);
blockPath.unshiftContainer("body", info.decl); blockPath.unshiftContainer("body", info.decl);
@ -220,11 +204,11 @@ function renameArguments(funcPath, argsId) {
} }
var argumentsVisitor = { var argumentsVisitor = {
"FunctionExpression|FunctionDeclaration": function(path) { "FunctionExpression|FunctionDeclaration": function FunctionExpressionFunctionDeclaration(path) {
path.skip(); path.skip();
}, },
Identifier: function(path, state) { Identifier: function Identifier(path, state) {
if (path.node.name === "arguments" && util.isReference(path)) { if (path.node.name === "arguments" && util.isReference(path)) {
path.replaceWith(state.argsId); path.replaceWith(state.argsId);
state.didRenameArguments = true; state.didRenameArguments = true;
@ -233,23 +217,17 @@ var argumentsVisitor = {
}; };
var awaitVisitor = { var awaitVisitor = {
Function: function(path) { Function: function Function(path) {
path.skip(); // Don't descend into nested function scopes. path.skip(); // Don't descend into nested function scopes.
}, },
AwaitExpression: function(path) { AwaitExpression: function AwaitExpression(path) {
// Convert await and await* expressions to yield expressions. // Convert await and await* expressions to yield expressions.
var argument = path.node.argument; var argument = path.node.argument;
// Transforming `await x` to `yield regeneratorRuntime.awrap(x)` // Transforming `await x` to `yield regeneratorRuntime.awrap(x)`
// causes the argument to be wrapped in such a way that the runtime // causes the argument to be wrapped in such a way that the runtime
// can distinguish between awaited and merely yielded values. // can distinguish between awaited and merely yielded values.
path.replaceWith(t.yieldExpression( path.replaceWith(t.yieldExpression(t.callExpression(util.runtimeProperty("awrap"), [argument]), false));
t.callExpression(
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", "name": "babel-plugin-transform-regenerator",
"description": "Source transformer enabling ECMAScript 6 generator functions (yield) in JavaScript-of-today (ES5)", "author": "Ben Newman <bn@cs.stanford.edu>",
"keywords": [ "description": "",
"generator",
"yield",
"coroutine",
"rewriting",
"transformation",
"syntax",
"codegen",
"rewriting",
"refactoring",
"transpiler",
"desugaring",
"ES6"
],
"version": "6.0.18", "version": "6.0.18",
"homepage": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator", "homepage": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator",
"repository": { "main": "lib/index.js",
"type": "git",
"url": "git://github.com/facebook/regenerator.git"
},
"main": "main.js",
"bin": "bin/regenerator",
"scripts": {
"test": "node test/run.js"
},
"dependencies": { "dependencies": {
"commoner": "~0.10.3", "commoner": "~0.10.3",
"babel-plugin-transform-es2015-block-scoping": "^6.0.18", "babel-plugin-transform-es2015-block-scoping": "^6.0.18",
@ -39,13 +17,5 @@
"private": "~0.1.5", "private": "~0.1.5",
"through": "~2.3.8" "through": "~2.3.8"
}, },
"devDependencies": { "license": "BSD"
"mocha": "~2.3.3",
"promise": "~7.0.4",
"semver": "~5.0.3"
},
"license": "BSD",
"engines": {
"node": ">= 0.6"
}
} }

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
));
}
};