diff --git a/packages/babel-plugin-transform-regenerator/.test/async.es6.js b/packages/babel-plugin-transform-regenerator/.test/async.es6.js deleted file mode 100644 index 587e9b5f00545..0000000000000 --- a/packages/babel-plugin-transform-regenerator/.test/async.es6.js +++ /dev/null @@ -1,493 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var assert = require("assert"); - -describe("async functions and await expressions", function() { - Promise = require("promise"); - - describe("regeneratorRuntime", function() { - it("should be defined globally", function() { - var global = Function("return this")(); - assert.ok("regeneratorRuntime" in global); - assert.strictEqual(global.regeneratorRuntime, regeneratorRuntime); - }); - - it("should have a .wrap method", function() { - assert.strictEqual(typeof regeneratorRuntime.wrap, "function"); - }); - }); - - describe("Promise", function() { - it("should be defined globally", function() { - var global = Function("return this")(); - assert.ok("Promise" in global); - assert.strictEqual(global.Promise, Promise); - }); - - it("should be a function", function() { - assert.strictEqual(typeof Promise, "function"); - }); - }); - - describe("no-await async function", function() { - it("should return a Promise", function(done) { - var called = false; - - async function noAwait(value) { - called = true; - return value; - } - - var promise = noAwait("asdf"); - assert.strictEqual(called, true); - - promise.then(function(value) { - assert.strictEqual(called, true); - assert.strictEqual(value, "asdf"); - done(); - }).catch(done); - }); - }); - - describe("one-await async function", function() { - it("should finish asynchronously", function(done) { - var flag1 = false; - var flag2 = false; - - async function oneAwait(value) { - flag1 = true; - var result = await value; - flag2 = true; - return result; - } - - var promise = oneAwait("asdf"); - assert.strictEqual(flag1, true); - assert.strictEqual(flag2, false); - - promise.then(function(value) { - assert.strictEqual(flag2, true); - assert.strictEqual(value, "asdf"); - done(); - }).catch(done); - }); - }); - - describe("nested async function calls", function() { - it("should evaluate in the right order", function(done) { - var markers = []; - - async function innerMost(marker) { - markers.push(marker); - return await marker; - } - - async function inner(marker) { - markers.push(marker); - - assert.strictEqual( - await innerMost(marker + 1), - marker + 1 - ); - - markers.push(marker + 2); - - assert.strictEqual( - await innerMost(marker + 3), - marker + 3 - ); - - markers.push(marker + 4); - } - - async function outer() { - markers.push(0); - await inner(1); - markers.push(6); - await inner(7); - markers.push(12); - } - - outer().then(function() { - var expected = []; - for (var i = 0; i <= 12; ++i) - expected.push(i); - assert.deepEqual(markers, expected); - done(); - }).catch(done); - }); - }); - - describe("dependent promises", function() { - it("should be awaitable out of order", function(done) { - async function outer(value) { - var resolved = false; - var p1 = new Promise(function(resolve) { - setTimeout(function() { - resolve(value + 1); - resolved = true; - }, 0); - }); - - assert.strictEqual(resolved, false); - - var v2 = await p1.then(function(value) { - return value + 1; - }); - - assert.strictEqual(resolved, true); - - var v1 = await p1; - - return [v1, v2]; - } - - outer(1).then(function(pair) { - assert.deepEqual(pair, [2, 3]); - done(); - }).catch(done); - }); - }); - - describe("rejected promises", function() { - it("should cause await expressions to throw", function(done) { - var error = new Error("rejected"); - - async function f(arg) { - try { - return await arg; - } catch (e) { - assert.strictEqual(e, error); - return "did throw"; - } - } - - Promise.all([ - f(Promise.reject(error)), - f(Promise.resolve("did not throw")) - ]).then(function(results) { - assert.deepEqual(results, [ - "did throw", - "did not throw" - ]); - done(); - }).catch(done); - }); - - it("should be returned by exceptional async functions", function(done) { - var error = new Error("rejected"); - - async function e(arg) { - if (arg) { - throw arg; - } - return "did not throw"; - } - - async function f(arg) { - return await e(arg); - } - - async function g(arg) { - return await f(arg); - } - - async function h(arg) { - return await Promise.all([ - g(arg), - Promise.resolve("dummy") - ]); - } - - Promise.all([ - h(error).then(function() { - done(new Error("should not have resolved")); - }, function(e) { - assert.strictEqual(e, error); - return "ok1"; - }), - h(null).then(function(result) { - assert.deepEqual(result, [ - "did not throw", - "dummy" - ]); - return "ok2"; - }) - ]).then(function(results) { - assert.deepEqual(results, ["ok1", "ok2"]); - done(); - }).catch(done); - }); - - it("should propagate failure when returned", function() { - var rejection = new Error("rejection"); - - async function f() { - return new Promise(function(resolve, reject) { - reject(rejection); - }); - } - - return f().then(function(result) { - assert.ok(false, "should have been rejected"); - }, function(error) { - assert.strictEqual(error, rejection); - }); - }); - }); - - describe("async function expressions", function() { - it("should be allowed", function(done) { - (async function(arg) { - return await arg; - })(Promise.resolve(1234)).then(function(value) { - assert.strictEqual(value, 1234); - done(); - }).catch(done); - }); - }); -}); - -describe("async generator functions", function() { - it("should return a working AsyncIterator", function() { - var markers = []; - - async function *gen(arg) { - markers.push(0); - var sent = yield arg; - markers.push(1); - var result = await sent; - markers.push(2); - assert.strictEqual(await (yield "second"), "sent after second"); - markers.push(3); - return result; - } - - var iter = gen("initial argument"); - assert.deepEqual(markers, []); - - var firstPromise = iter.next(); - assert.deepEqual(markers, [0]); - - return firstPromise.then(function(firstResult) { - assert.deepEqual(firstResult, { - value: "initial argument", - done: false - }); - - assert.deepEqual(markers, [0]); - - return iter.next(new Promise(function(resolve) { - setTimeout(resolve, 100); - }).then(function() { - assert.deepEqual(markers, [0, 1]); - return "will become final result"; - })); - - }).then(function(secondResult) { - assert.deepEqual(secondResult, { - value: "second", - done: false - }); - - assert.deepEqual(markers, [0, 1, 2]); - - return iter.next("sent after second"); - - }).then(function(finalResult) { - assert.deepEqual(markers, [0, 1, 2, 3]); - assert.deepEqual(finalResult, { - value: "will become final result", - done: true - }); - }); - }); - - it("should keep results in order", function() { - async function *range(limit) { - var before = []; - var after = []; - for (var i = 0; i < limit; ++i) { - before.push(i); - yield i; - after.push(i); - } - assert.deepEqual(before, after); - return before; - } - - var limit = 10; - var iter = range(limit); - var promises = []; - var results = []; - - for (var i = 0; i < limit; ++i) { - var promise = iter.next(); - promises.push(promise); - - promise.then(function(result) { - assert.strictEqual(result.done, false); - results.push(result); - }); - } - - assert.deepEqual(results, []); - - return Promise.all(promises).then(function(promiseResults) { - assert.deepEqual(results, promiseResults); - - return iter.next(); - - }).then(function(finalResult) { - assert.deepEqual(results.map(function(result) { - return result.value; - }), finalResult.value); - - assert.strictEqual(finalResult.done, true); - }); - }); - - it("should be able to handle many awaits", function() { - var awaitCount = 0; - - function countAwait(i) { - return Promise.resolve(i).then(function() { - ++awaitCount; - }); - } - - async function *gen(limit) { - await countAwait(0); - yield 1; - await countAwait(2); - await countAwait(3); - yield 4; - await countAwait(5); - await countAwait(6); - await countAwait(7); - yield 8; - for (var i = 0; i < limit; ++i) { - await countAwait(i); - } - return "done"; - } - - var iter = gen(100); - - return iter.next().then(function(result) { - assert.strictEqual(awaitCount, 1); - - assert.deepEqual(result, { - value: 1, - done: false - }); - - return iter.next(); - - }).then(function(result) { - assert.strictEqual(awaitCount, 3); - - assert.deepEqual(result, { - value: 4, - done: false - }); - - return iter.next(); - - }).then(function(result) { - assert.strictEqual(awaitCount, 6); - - assert.deepEqual(result, { - value: 8, - done: false - }); - - return iter.next(); - - }).then(function(result) { - assert.strictEqual(awaitCount, 6 + 100); - - assert.deepEqual(result, { - value: "done", - done: true - }); - - return iter.next(); - - }).then(function(result) { - assert.deepEqual(result, { - value: void 0, - done: true - }); - }); - }); - - it("should not propagate exceptions between iterations", function() { - async function *gen() { - yield 1; - yield 2; - } - - var iter = gen(); - - return iter.next().then(function(result) { - assert.deepEqual(result, { - value: 1, - done: false - }); - - return iter.throw(new Error("thrown from first yield")); - - }).then(function() { - throw new Error("should have thrown"); - - }, function(error) { - assert.strictEqual(error.message, "thrown from first yield"); - return iter.next(); - - }).then(function(result) { - assert.deepEqual(result, { - value: void 0, - done: true - }); - }); - }); - - it("should allow yielding a rejected Promise", function() { - var yielded = new Error("yielded rejection"); - var returned = new Error("returned rejection"); - - async function *gen() { - assert.strictEqual(yield Promise.reject(yielded), "first sent"); - assert.strictEqual(yield "middle", "second sent"); - return Promise.reject(returned); - } - - var iter = gen(); - - return iter.next().then(function(result) { - assert.ok(false, "should have yielded a rejected Promise"); - }, function(error) { - assert.strictEqual(error, yielded); - return iter.next("first sent"); - }).then(function(result) { - assert.deepEqual(result, { - value: "middle", - done: false - }); - return iter.next("second sent"); - }).then(function(result) { - assert.ok(false, "should have returned a rejected Promise"); - }, function(error) { - assert.strictEqual(error, returned); - }); - }); -}); diff --git a/packages/babel-plugin-transform-regenerator/.test/tests.es6.js b/packages/babel-plugin-transform-regenerator/.test/tests.es6.js deleted file mode 100644 index 3df59e76e9aba..0000000000000 --- a/packages/babel-plugin-transform-regenerator/.test/tests.es6.js +++ /dev/null @@ -1,2560 +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"); -var runningInTranslation = /\.wrap\(/.test(function*(){}); -var iteratorSymbol = typeof Symbol === "function" - && Symbol.iterator - || "@@iterator"; - -function check(g, yields, returnValue) { - for (var i = 0; i < yields.length; ++i) { - var info = g.next(i); - assert.deepEqual(info.value, yields[i]); - assert.strictEqual(info.done, false); - } - - assert.deepEqual( - i > 0 ? g.next(i) : g.next(), - { value: returnValue, done: true } - ); -} - -// A version of `throw` whose behavior can't be statically analyzed. -// Useful for testing dynamic exception dispatching. -function raise(argument) { - throw argument; -} - -function assertAlreadyFinished(generator) { - assert.deepEqual(generator.next(), { - value: void 0, - done: true - }); -} - -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"); - }); - - it("should have a .mark method", function() { - assert.strictEqual(typeof regeneratorRuntime.mark, "function"); - }); - - it("should be the object name returned by util.runtimeProperty", function() { - assert.strictEqual( - require("../lib/util").runtimeProperty("foo").object.name, - "regeneratorRuntime" - ); - }); -}); - -(runningInTranslation ? describe : xdescribe)("@@iterator", function() { - it("is defined on Generator.prototype and returns this", function() { - function *gen(){} - var iterator = gen(); - assert.ok(!iterator.hasOwnProperty(iteratorSymbol)); - assert.ok(!Object.getPrototypeOf(iterator).hasOwnProperty(iteratorSymbol)); - assert.ok(Object.getPrototypeOf( - Object.getPrototypeOf(iterator) - ).hasOwnProperty(iteratorSymbol)); - assert.strictEqual(iterator[iteratorSymbol](), iterator); - }); -}); - -describe("simple argument yielder", function() { - it("should yield only its first argument", function() { - function *gen(x) { - yield x; - } - - check(gen("oyez"), ["oyez"]); - check(gen("foo", "bar"), ["foo"]); - }); - - it("should support multiple yields in expression", function() { - function *gen() { return (yield 0) + (yield 0); } - var itr = gen(); - itr.next(); - itr.next(1); - assert.equal(itr.next(2).value, 3); - }); -}); - -function *range(n) { - for (var i = 0; i < n; ++i) { - yield i; - } -} - -describe("range generator", function() { - it("should yield the empty range", function() { - check(range(0), []); - }) - - it("should yield the range 0..n-1", function() { - check(range(5), [0, 1, 2, 3, 4]); - }); -}); - -describe("collatz generator", function() { - function *gen(n) { - var count = 0; - - yield n; - - while (n !== 1) { - count += 1; - - if (n % 2) { - yield n = n * 3 + 1; - } else { - yield n >>= 1; - } - } - - return count; - } - - function collatz(n) { - var result = [n]; - - while (n !== 1) { - if (n % 2) { - n *= 3; - n += 1; - } else { - n >>= 1; - } - - result.push(n); - } - - return result; - } - - var seven = collatz(7); - var fiftyTwo = seven.slice(seven.indexOf(52)); - var eightyTwo = collatz(82); - - it("seven", function() { - check(gen(7), seven, 16); - }); - - it("fifty two", function() { - check(gen(52), fiftyTwo, 11); - }); - - it("eighty two", function() { - check(gen(82), eightyTwo, 110); - }); -}); - -describe("throw", function() { - (runningInTranslation ? it : xit)("should complete generator", function() { - function *gen(x) { - throw 1; - } - - var u = gen(); - - try { - u.next(); - } catch (err) { - assert.strictEqual(err, 1); - } - - assertAlreadyFinished(u); - }); -}); - -describe("try-catch generator", function() { - function *usingThrow(x) { - yield 0; - try { - yield 1; - if (x % 2 === 0) - throw 2; - yield x; - } catch (x) { - yield x; - } - yield 3; - } - - function *usingRaise(x) { - yield 0; - try { - yield 1; - if (x % 2 === 0) - raise(2); - yield x; - } catch (x) { - yield x; - } - yield 3; - } - - it("should catch static exceptions properly", function() { - check(usingThrow(4), [0, 1, 2, 3]); - check(usingThrow(5), [0, 1, 5, 3]); - }); - - it("should catch dynamic exceptions properly", function() { - check(usingRaise(4), [0, 1, 2, 3]); - check(usingRaise(5), [0, 1, 5, 3]); - }); -}); - -describe("nested generators in try-catch", function() { - function *gen() { - try { - nonExistent; - } catch (e) { - yield function* () { - yield e; - } - } - } - - it('should get a reference to the caught error', function () { - var genFun2 = gen().next().value; - assert.ok(regeneratorRuntime.isGeneratorFunction(genFun2)); - var gen2 = genFun2(); - var res = gen2.next(); - assert.ok(res.value instanceof ReferenceError); - // Note that we don't do strict equality over the message because it varies - // across browsers (if we ever want to run tests in browsers). - assert.ok(res.value.message.match(/nonExistent/)); - }); - -}); - -describe("try-finally generator", function() { - function *usingThrow(condition) { - yield 0; - try { - yield 1; - throw 2; - yield 3; - } finally { - if (condition) { - yield 4; - return 5; - } - yield 6; - return 7; - } - } - - function *usingRaise(condition) { - yield 0; - try { - yield 1; - raise(2); - yield 3; - } finally { - if (condition) { - yield 4; - return 5; - } - yield 6; - return 7; - } - } - - function *usingAbrupt(abruptType, finallyAbruptType) { - yield 0; - for (;;) { - try { - yield 1; - if (abruptType === "return") { - return 2; - } else if (abruptType === "break") { - break; - } else if (abruptType === "continue") { - abruptType = "return"; - continue; - } - } - finally { - yield 3; - if (finallyAbruptType === "return") { - return 4; - } else if (finallyAbruptType === "break") { - break; - } else if (finallyAbruptType === "continue") { - finallyAbruptType = null; - continue; - } - } - } - return 5; - } - - it("should honor return", function() { - check(usingAbrupt("return", null), [0, 1, 3], 2); - }); - - it("should honor break", function() { - check(usingAbrupt("break", null), [0, 1, 3], 5); - }); - - it("should honor continue", function() { - check(usingAbrupt("continue", null), [0, 1, 3, 1, 3], 2); - }); - - it("should override abrupt with return", function() { - check(usingAbrupt("return", "return"), [0, 1, 3], 4); - check(usingAbrupt("break", "return"), [0, 1, 3], 4); - check(usingAbrupt("continue", "return"), [0, 1, 3], 4); - }); - - it("should override abrupt with break", function() { - check(usingAbrupt("return", "break"), [0, 1, 3], 5); - check(usingAbrupt("break", "break"), [0, 1, 3], 5); - check(usingAbrupt("continue", "break"), [0, 1, 3], 5); - }); - - it("should override abrupt with continue", function() { - check(usingAbrupt("return", "continue"), [0, 1, 3, 1, 3], 2); - check(usingAbrupt("break", "continue"), [0, 1, 3, 1, 3], 5); - check(usingAbrupt("continue", "continue"), [0, 1, 3, 1, 3], 2); - }); - - it("should execute finally blocks statically", function() { - check(usingThrow(true), [0, 1, 4], 5); - check(usingThrow(false), [0, 1, 6], 7); - }); - - it("should execute finally blocks dynamically", function() { - check(usingRaise(true), [0, 1, 4], 5); - check(usingRaise(false), [0, 1, 6], 7); - }); - - it("should execute finally blocks before throwing", function() { - var uncaughtError = new Error("uncaught"); - - function *uncaught(condition) { - try { - yield 0; - if (condition) { - yield 1; - raise(uncaughtError); - } - yield 2; - } finally { - yield 3; - } - yield 4; - } - - check(uncaught(false), [0, 2, 3, 4]); - - var u = uncaught(true); - - assert.deepEqual(u.next(), { value: 0, done: false }); - assert.deepEqual(u.next(), { value: 1, done: false }); - assert.deepEqual(u.next(), { value: 3, done: false }); - - try { - u.next(); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, uncaughtError); - } - }); - - it("should throw correct error when finally contains catch", function() { - var right = new Error("right"); - var wrong = new Error("wrong"); - - function *gen() { - try { - yield 0; - raise(right); - } finally { - yield 1; - try { - raise(wrong); - } catch (err) { - assert.strictEqual(err, wrong); - yield 2; - } - } - } - - var g = gen(); - - assert.deepEqual(g.next(), { - value: 0, - done: false - }); - - assert.deepEqual(g.next(), { - value: 1, - done: false - }); - - assert.deepEqual(g.next(), { - value: 2, - done: false - }); - - try { - g.next(); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, right); - } - }); - - it("should run finally after break within try", function() { - function *gen() { - try { - yield 0; - while (true) { - yield 1; - break; - } - } finally { - yield 2; - } - yield 3; - } - - check(gen(), [0, 1, 2, 3]); - }); -}); - -describe("try-catch-finally generator", function() { - function *usingThrow() { - yield 0; - try { - try { - yield 1; - throw 2; - yield 3; - } catch (x) { - throw yield x; - } finally { - yield 5; - } - } catch (thrown) { - yield thrown; - } - yield 6; - } - - function *usingRaise() { - yield 0; - try { - try { - yield 1; - raise(2); - yield 3; - } catch (x) { - throw yield x; - } finally { - yield 5; - } - } catch (thrown) { - yield thrown; - } - yield 6; - } - - it("should statically catch and then finalize", function() { - check(usingThrow(), [0, 1, 2, 5, 3, 6]); - }); - - it("should dynamically catch and then finalize", function() { - check(usingRaise(), [0, 1, 2, 5, 3, 6]); - }); - - it("should execute catch and finally blocks at most once", function() { - var error = new Error(); - - function *gen() { - try { - switch (1) { - case 1: - yield "a"; - break; - default: - break; - } - throw error; - } catch (e) { - assert.strictEqual(e, error); - yield "b"; - do { - do { - yield "c"; - break; - } while (false); - yield "d"; - break; - } while (false); - yield "e"; - } finally { - yield "f"; - } - } - - check(gen(), ["a", "b", "c", "d", "e", "f"]); - }); - - it("should handle backwards jumps in labeled loops", function() { - function *gen() { - var firstTime = true; - outer: - while (true) { - yield 0; - try { - while (true) { - yield 1; - if (firstTime) { - firstTime = false; - yield 2; - continue outer; - } else { - yield 3; - break; - } - } - yield 4; - break; - } finally { - yield 5; - } - yield 6; - } - yield 7; - } - - check(gen(), [0, 1, 2, 5, 0, 1, 3, 4, 5, 7]); - }); - - it("should handle loop continue statements properly", function() { - var error = new Error("thrown"); - var markers = []; - - function *gen() { - var c = 2; - while (c > 0) { - try { - markers.push("try"); - yield c; - } catch (e) { - assert.strictEqual(e, error); - markers.push("catch"); - continue; - } finally { - markers.push("finally"); - } - markers.push("decrement"); - --c; - } - } - - var g = gen(); - - assert.deepEqual(g.next(), { value: 2, done: false }); - assert.deepEqual(g.throw(error), { value: 2, done: false }); - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: void 0, done: true }); - - assert.deepEqual(markers, [ - "try", - "catch", - "finally", - "try", - "finally", - "decrement", - "try", - "finally", - "decrement" - ]); - }); -}); - -describe("dynamic exception", function() { - function *gen(x, fname) { - try { - return fns[fname](x); - } catch (thrown) { - yield thrown; - } - } - - var fns = { - f: function(x) { - throw x; - }, - - g: function(x) { - return x; - } - }; - - it("should be dispatched correctly", function() { - check(gen("asdf", "f"), ["asdf"]); - check(gen("asdf", "g"), [], "asdf"); - }); -}); - -describe("nested finally blocks", function() { - function *usingThrow() { - try { - try { - try { - throw "thrown"; - } finally { - yield 1; - } - } catch (thrown) { - yield thrown; - } finally { - yield 2; - } - } finally { - yield 3; - } - } - - function *usingRaise() { - try { - try { - try { - raise("thrown"); - } finally { - yield 1; - } - } catch (thrown) { - yield thrown; - } finally { - yield 2; - } - } finally { - yield 3; - } - } - - it("should statically execute in order", function() { - check(usingThrow(), [1, "thrown", 2, 3]); - }); - - it("should dynamically execute in order", function() { - check(usingRaise(), [1, "thrown", 2, 3]); - }); -}); - -describe("for-in loop generator", function() { - it("should handle the simple case", function() { - function *gen() { - var count = 0; - var obj = {foo: 1, bar: 2}; - for (var key in obj) { - assert(obj.hasOwnProperty(key), key + " must be own property"); - yield [key, obj[key]]; - count += 1; - } - return count; - } - - check(gen(), [["foo", 1], ["bar", 2]], 2); - }); - - it("should handle break in loop", function() { - function *gen(obj) { - var count = 0; - for (var key in (yield "why not", obj)) { - if (obj.hasOwnProperty(key)) { - if (key === "skip") { - break; - } - count += 1; - yield [key, obj[key]]; - } - } - return count; - } - - check( - gen({ a: 1, b: 2, skip: 3, c: 4 }), - ["why not", ["a", 1], ["b", 2]], - 2 - ); - }); - - it("should handle property deletion in loop", function() { - function *gen() { - var count = 0; - var obj = {foo: 1, bar: 2}; - for (var key in obj) { - assert(obj.hasOwnProperty(key), key + " must be own property"); - yield [key, obj[key]]; - delete obj.bar; - count += 1; - } - return count; - } - - check(gen(), [["foo", 1]], 1); - }); - - it("should loop over inherited properties", function() { - function *gen() { - var count = 0; - function Foo() { - this.baz = 1 - } - Foo.prototype.bar = 2; - - var foo = new Foo(); - for (var key in foo) { - yield [key, foo[key]]; - count += 1; - } - return count; - } - - check(gen(), [["baz", 1], ["bar", 2]], 2); - }); - - it("should handle risky object expressions", function() { - function a(sent) { - assert.strictEqual(sent, 1); - a.called = true; - } - - function b(sent) { - assert.strictEqual(sent, 2); - b.called = true; - return { callee: b }; - } - - function *gen() { - assert.ok(!a.called); - assert.ok(!b.called); - for (var key in a(yield 0), b(yield 1)) { - assert.ok(a.called); - assert.ok(b.called); - assert.strictEqual(yield key, 3); - } - - for (var key in a(1), { foo: "foo", bar: "bar" }) { - yield key; - } - } - - check(gen(), [0, 1, "callee", "foo", "bar"]); - }); - - it("should allow non-Identifier left-hand expressions", function() { - var obj = {}; - var baz = { a: 1, b: 2, c: 3 }; - var markers = []; - - function foo() { - markers.push("called foo"); - return obj; - } - - function *gen() { - for (foo().bar in baz) { - markers.push(obj.bar); - yield obj.bar; - } - } - - check(gen(), ["a", "b", "c"]); - - assert.deepEqual(markers, [ - "called foo", - "a", - "called foo", - "b", - "called foo", - "c" - ]); - }); -}); - -describe("yield chain", function() { - function *gen(n) { - return yield yield yield yield n; - } - - it("should have correct associativity", function() { - check(gen(5), [5, 1, 2, 3], 4); - check(gen("asdf"), ["asdf", 1, 2, 3], 4); - }); -}); - -describe("object literal generator", function() { - function *gen(a, b) { - yield { - a: a - (yield a), - b: yield b - }; - } - - it("should yield the correct object", function() { - check(gen(1, 2), [1, 2, { a: 0, b: 2 }]); - check(gen(4, 2), [4, 2, { a: 3, b: 2 }]); - }); -}); - -describe("switch statement generator", function() { - function *gen(a) { - switch (yield a) { - case (yield "x") - a: - return "first case"; - case (yield "y") - a: - return "second case"; - } - } - - it("should jump to the correct cases", function() { - check(gen(1), [1, "x"], "first case"); - check(gen(2), [2, "x", "y"], "second case"); - }); -}); - -describe("infinite sequence generator", function() { - function *gen(start, step) { - step = step || 1; - while (true) { - yield start; - start += step; - } - } - - function *limit(g, stop) { - while (true) { - var info = g.next(); - if (info.done) { - return; - } else if (info.value < stop) { - yield info.value; - } else { - return; - } - } - } - - it("should generate a lot of plausible values", function() { - var g = gen(10, 2); - - assert.deepEqual(g.next(), { value: 10, done: false }); - assert.deepEqual(g.next(), { value: 12, done: false }); - assert.deepEqual(g.next(), { value: 14, done: false }); - assert.deepEqual(g.next(), { value: 16, done: false }); - - var sum = 10 + 12 + 14 + 16; - - for (var n = 0; n < 1000; ++n) { - var info = g.next(); - sum += info.value; - assert.strictEqual(info.done, false); - } - - assert.strictEqual(sum, 1017052); - }); - - it("should allow limiting", function() { - check(limit(gen(10, 3), 20), [10, 13, 16, 19]); - }); -}); - -describe("generator function expression", function() { - it("should behave just like a declared generator", function() { - check(function *(x, y) { - yield x; - yield y; - yield x + y; - return x * y; - }(3, 7), [3, 7, 10], 21); - }) -}); - -describe("generator reentry attempt", function() { - function *gen(x) { - try { - (yield x).next(x); - } catch (err) { - yield err; - } - return x + 1; - } - - it("should complain with a TypeError", function() { - var g = gen(3); - assert.deepEqual(g.next(), { value: 3, done: false }); - var complaint = g.next(g); // Sending the generator to itself. - assert.ok(complaint.value instanceof Error); - assert.strictEqual( - complaint.value.message, - "Generator is already running" - ); - assert.deepEqual(g.next(), { value: 4, done: true }); - }); -}); - -describe("completed generator", function() { - function *gen() { - return "ALL DONE"; - } - - (runningInTranslation ? it : xit) - ("should refuse to resume", function() { - var g = gen(); - - assert.deepEqual(g.next(), { - value: "ALL DONE", done: true - }); - - assertAlreadyFinished(g); - }); -}); - -describe("delegated yield", function() { - it("should delegate correctly", function() { - function *gen(condition) { - yield 0; - if (condition) { - yield 1; - yield* gen(false); - yield 2; - } - yield 3; - } - - check(gen(true), [0, 1, 0, 3, 2, 3]); - check(gen(false), [0, 3]); - }); - - it("should cope with empty delegatees", function() { - function *gen(condition) { - if (condition) { - yield 0; - yield* gen(false); - yield 1; - } - } - - check(gen(true), [0, 1]); - check(gen(false), []); - }); - - it("should support deeper nesting", function() { - function *outer(n) { - yield n; - yield* middle(n - 1, inner(n + 10)); - yield n + 1; - } - - function *middle(n, plusTen) { - yield n; - yield* inner(n - 1); - yield n + 1; - yield* plusTen; - } - - function *inner(n) { - yield n; - } - - check(outer(5), [5, 4, 3, 5, 15, 6]); - }); - - it("should pass sent values through", function() { - function *outer(n) { - yield* inner(n << 1); - yield "zxcv"; - } - - function *inner(n) { - return yield yield yield n; - } - - var g = outer(3); - assert.deepEqual(g.next(), { value: 6, done: false }); - assert.deepEqual(g.next(1), { value: 1, done: false }); - assert.deepEqual(g.next(2), { value: 2, done: false }); - assert.deepEqual(g.next(4), { value: "zxcv", done: false }); - assert.deepEqual(g.next(5), { value: void 0, done: true }); - }); - - it("should be governed by enclosing try statements", function() { - var error = new Error("thrown"); - - function *outer(n) { - try { - yield 0; - yield* inner(n); - yield 1; - } catch (err) { - yield err.message; - } - yield 4; - } - - function *inner(n) { - while (n --> 0) { - try { - if (n === 3) { - raise(error); - } - } finally { - yield n; - } - } - } - - check(outer(3), [0, 2, 1, 0, 1, 4]); - check(outer(5), [0, 4, 3, "thrown", 4]); - }); - - it("should dispatch .thrown exceptions correctly", function() { - var count = 0; - - function *gen() { - yield* inner(); - try { - yield* inner(); - } catch (err) { - // pass - } - return yield* inner(); - } - - function *inner() { - return yield count++; - } - - var g = gen(); - - assert.deepEqual(g.next(), { - value: 0, - done: false - }); - - assert.deepEqual(g.next(), { - value: 1, - done: false - }); - - assert.deepEqual(g.throw(new Error("lol")), { - value: 2, - done: false, - }); - - assert.deepEqual(g.next("sent"), { - value: "sent", - done: true - }); - }); - - it("should call .return methods of delegate iterators", function() { - var throwee = new Error("argument to gen.throw"); - var thrownFromThrow = new Error("thrown from throw method"); - var thrownFromReturn = new Error("thrown from return method"); - - function *gen(delegate) { - try { - return yield* delegate; - } catch (err) { - return err; - } - } - - function check(throwMethod, returnMethod) { - var throwCalled = false; - var returnCalled = false; - var count = 0; - var iterator = { - next: function() { - return { value: count++, done: false }; - } - }; - - iterator[iteratorSymbol] = function() { - return this; - }; - - if (throwMethod) { - iterator["throw"] = function() { - throwCalled = true; - return throwMethod.apply(this, arguments); - }; - } - - if (returnMethod) { - iterator["return"] = function() { - returnCalled = true; - return returnMethod.apply(this, arguments); - }; - } - - var g = gen(iterator); - - assert.deepEqual(g.next(), { value: 0, done: false }); - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: 2, done: false }); - assert.deepEqual(g.next(), { value: 3, done: false }); - - assert.strictEqual(throwCalled, false); - assert.strictEqual(returnCalled, false); - - var result = {}; - - result.throwResult = g.throw(throwee); - result.throwCalled = throwCalled; - result.returnCalled = returnCalled; - - return result; - } - - var checkResult = check(undefined, function() { - throw thrownFromReturn; - }); - if (runningInTranslation) { - // BUG: Node v0.11 and v0.12 neglect to call .return here. - assert.strictEqual(checkResult.throwResult.value, thrownFromReturn); - } else { - // This is the TypeError that results from trying to call the - // undefined .throw method of the iterator. - assert.ok(checkResult.throwResult.value instanceof TypeError); - } - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, false); - // BUG: Node v0.11 and v0.12 neglect to call .return here. - assert.strictEqual(checkResult.returnCalled, runningInTranslation); - - checkResult = check(undefined, function() { - return { value: "from return", done: true }; - }); - assert.notStrictEqual(checkResult.throwResult.value, throwee); - // This is the TypeError that results from trying to call the - // undefined .throw method of the iterator. - assert.ok(checkResult.throwResult.value instanceof TypeError); - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, false); - // BUG: Node v0.11 and v0.12 neglect to call .return here. - assert.strictEqual(checkResult.returnCalled, runningInTranslation); - - var checkResult = check(function(thrown) { - return { value: "from throw", done: true }; - }, function() { - throw thrownFromReturn; - }); - assert.strictEqual(checkResult.throwResult.value, "from throw"); - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, true); - assert.strictEqual(checkResult.returnCalled, false); - - var checkResult = check(function(thrown) { - throw thrownFromThrow; - }, function() { - throw thrownFromReturn; - }); - assert.strictEqual(checkResult.throwResult.value, thrownFromThrow); - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, true); - assert.strictEqual(checkResult.returnCalled, false); - - var checkResult = check(undefined, undefined); - assert.notStrictEqual(checkResult.throwResult.value, throwee); - // This is the TypeError that results from trying to call the - // undefined .throw method of the iterator. - assert.ok(checkResult.throwResult.value instanceof TypeError); - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, false); - assert.strictEqual(checkResult.returnCalled, false); - }); - - it("should not be required to have a .return method", function() { - function *gen(delegate) { - return yield* delegate; - } - - var inner = range(5); - var iterator = { next: inner.next.bind(inner) }; - iterator[iteratorSymbol] = function() { - return this; - }; - - var g = gen(iterator); - assert.deepEqual(g.next(), { value: 0, done: false }); - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: 2, done: false }); - - if (typeof g.return === "function") { - assert.deepEqual(g.return(-1), { value: -1, done: true }); - assert.deepEqual(g.next(), { value: void 0, done: true }); - } - }); - - (runningInTranslation ? it : xit) - ("should support any iterable argument", function() { - function *gen() { - yield 0; - yield* [ - yield "one", - yield "two", - yield "three" - ]; - yield 5; - } - - check(gen(), [0, "one", "two", "three", 2, 3, 4, 5]); - - function *string() { - return yield* "asdf"; - } - - check(string(), ["a", "s", "d", "f"]); - }); - - it("should evaluate to the return value of the delegate", function() { - function *inner() { - yield 1; - return 2; - } - - function *outer(delegate) { - return yield* delegate; - } - - check(outer(inner()), [1], 2); - - var arrayDelegate = [3, 4]; - if (!runningInTranslation) { - // Node v0.11 doesn't know how to turn arrays into iterators over - // their elements without a little help. - arrayDelegate = regeneratorRuntime.values(arrayDelegate); - } - check(outer(arrayDelegate), [3, 4], void 0); // See issue #143. - - if (!runningInTranslation) { - return; - } - - check(outer({ - next: function() { - return { value: "oyez", done: true }; - } - }), [], "oyez"); - }); - - it("should work as a subexpression", function() { - function *inner(arg) { - return arg; - } - - function *gen(delegate) { - // Unfortunately these parentheses appear to be necessary. - return 1 + (yield* delegate); - } - - check(gen(inner(2)), [], 3); - check(gen(inner(3)), [], 4); - - if (!runningInTranslation) { - return; - } - - check(gen({ - next: function() { - return { value: "foo", done: true }; - } - }), [], "1foo"); - }); -}); - -describe("function declaration hoisting", function() { - it("should work even if the declarations are out of order", function() { - function *gen(n) { - yield increment(n); - - function increment(x) { - return x + 1; - } - - if (n % 2) { - yield halve(decrement(n)); - - function halve(x) { - return x >> 1; - } - - function decrement(x) { - return x - 1; - } - } else { - // The behavior of function declarations nested inside conditional - // blocks is notoriously underspecified, and in V8 it appears the - // halve function is still defined when we take this branch, so - // "undefine" it for consistency with regenerator semantics. - halve = void 0; - } - - yield typeof halve; - - yield increment(increment(n)); - } - - check(gen(3), [4, 1, "function", 5]); - check(gen(4), [5, "undefined", 6]); - }); - - it("should work for nested generator function declarations", function() { - function *outer(n) { - yield 0; - assert.ok(regeneratorRuntime.isGeneratorFunction(inner)); - return yield* inner(n); - - // Note that this function declaration comes after everything else - // in the outer function, but needs to be fully available above. - function *inner(n) { - yield n - 1; - yield n; - return yield n + 1; - } - } - - check(outer(2), [0, 1, 2, 3], 4); - }); - - it("should not interfere with function rebinding", function() { - function rebindTo(value) { - var oldValue = toBeRebound; - toBeRebound = value; - return oldValue; - } - - function *toBeRebound() { - var originalValue = toBeRebound; - yield toBeRebound; - assert.strictEqual(rebindTo(42), originalValue); - yield toBeRebound; - assert.strictEqual(rebindTo("asdf"), 42); - yield toBeRebound; - } - - var original = toBeRebound; - check(toBeRebound(), [original, 42, "asdf"]); - - function attemptToRebind(value) { - var oldValue = safe; - safe = value; - return oldValue; - } - - var safe = function *safe() { - var originalValue = safe; - yield safe; - assert.strictEqual(attemptToRebind(42), originalValue); - yield safe; - assert.strictEqual(attemptToRebind("asdf"), 42); - yield safe; - } - - original = safe; - check(safe(), [safe, safe, safe]); - }); -}); - -describe("the arguments object", function() { - it("should work in simple variadic functions", function() { - function *sum() { - var result = 0; - - for (var i = 0; i < arguments.length; ++i) { - yield result += arguments[i]; - } - - return result; - } - - check(sum(1, 2, 3), [1, 3, 6], 6); - check(sum(9, -5, 3, 0, 2), [9, 4, 7, 7, 9], 9); - }); - - it("should alias function parameters", function() { - function *gen(x, y) { - yield x; - ++arguments[0]; - yield x; - - yield y; - --arguments[1]; - yield y; - - var temp = y; - y = x; - x = temp; - - yield x; - yield y; - } - - check(gen(3, 7), [3, 4, 7, 6, 6, 4]); - check(gen(10, -5), [10, 11, -5, -6, -6, 11]); - }); - - it("should be shadowable by explicit declarations", function() { - function *asParameter(x, arguments) { - arguments = arguments + 1; - yield x + arguments; - } - - check(asParameter(4, 5), [10]); - check(asParameter("asdf", "zxcv"), ["asdfzxcv1"]); - - function *asVariable(x) { - // TODO References to arguments before the variable declaration - // seem to see the object instead of the undefined value. - var arguments = x + 1; - yield arguments; - } - - check(asVariable(4), [5]); - check(asVariable("asdf"), ["asdf1"]); - }); - - it("should not get confused by properties", function() { - function *gen(args) { - var obj = { arguments: args }; - yield obj.arguments; - obj.arguments = "oyez"; - yield obj; - } - - check(gen(42), [42, { arguments: "oyez" }]); - }); - - it("supports .callee", function() { - function *gen(doYield) { - yield 1; - if (doYield) { - yield 2; - } else { - yield 3 - yield* arguments.callee(true); - yield 4 - } - yield 5; - } - - check(gen(false), [1, 3, 1, 2, 5, 4, 5]); - }); -}); - -describe("catch parameter shadowing", function() { - it("should leave outer variables unmodified", function() { - function *gen(x) { - var y = x + 1; - try { - throw x + 2; - } catch (x) { - yield x; - x += 1; - yield x; - } - yield x; - try { - throw x + 3; - } catch (y) { - yield y; - y *= 2; - yield y; - } - yield y; - } - - check(gen(1), [3, 4, 1, 4, 8, 2]); - check(gen(2), [4, 5, 2, 5, 10, 3]); - }); - - it("should not replace variables defined in inner scopes", function() { - function *gen(x) { - try { - throw x; - } catch (x) { - yield x; - - yield (function(x) { - return x += 1; - }(x + 1)); - - yield (function() { - var x = arguments[0]; - return x * 2; - }(x + 2)); - - yield (function() { - function notCalled(x) { - throw x; - } - - x >>= 1; - return x; - }()); - - yield x -= 1; - } - - yield x; - } - - check(gen(10), [10, 12, 24, 5, 4, 10]); - check(gen(11), [11, 13, 26, 5, 4, 11]); - }); - - it("should allow nested catch parameters of the same name", function() { - function *gen() { - try { - raise("e1"); - } catch (e) { - yield e; - try { - raise("e2"); - } catch (e) { - yield e; - } - yield e; - } - } - - check(gen(), ["e1", "e2", "e1"]); - }); - - it("should not interfere with non-referential identifiers", function() { - function *gen() { - try { - yield 1; - raise(new Error("oyez")); - yield 2; - } catch (e) { - yield 3; - e.e = "e.e"; - e[e.message] = "e.oyez"; - return { - e: e, - identity: function(x) { - var e = x; - return e; - } - }; - } - yield 4; - } - - var g = gen(); - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: 3, done: false }); - - var info = g.next(); - assert.strictEqual(info.done, true); - assert.strictEqual(info.value.e.message, "oyez"); - assert.strictEqual(info.value.e.e, "e.e"); - assert.strictEqual(info.value.e.oyez, "e.oyez"); - assert.strictEqual(info.value.identity("same"), "same"); - }); -}); - -describe("empty while loops", function() { - it("should be preserved in generated code", function() { - function *gen(x) { - while (x) { - // empty while loop - } - - do { - // empty do-while loop - } while (x); - - return gen.toString(); - } - - var info = gen(false).next(); - assert.strictEqual(info.done, true); - assert.ok(/empty while loop/.test(info.value)); - assert.ok(/empty do-while loop/.test(info.value)); - }); -}); - -describe("object literals with multiple yields", function() { - it("should receive different sent values", function() { - function *gen(fn) { - return { - a: yield "a", - b: yield "b", - c: fn(yield "c", yield "d"), - d: [yield "e", yield "f"] - }; - } - - check(gen(function sum(x, y) { - return x + y; - }), ["a", "b", "c", "d", "e", "f"], { - a: 1, - b: 2, - c: 3 + 4, - d: [5, 6] - }); - }); -}); - -describe("generator .throw method", function() { - (runningInTranslation ? it : xit)("should complete generator", function() { - function *gen(x) { - yield 2; - throw 1; - } - - var u = gen(); - - u.next(); - - try { - u.throw(2); - } catch (err) { - assert.strictEqual(err, 2); - } - - assertAlreadyFinished(u); - }); - - it("should work after the final call to .next", function() { - function *gen() { - yield 1; - } - - var g = gen(); - assert.deepEqual(g.next(), { value: 1, done: false }); - - var exception = new Error("unhandled exception"); - try { - g.throw(exception); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, exception); - } - }); - - it("should immediately complete a new-born generator", function() { - var began = false; - - function *gen() { - began = true; - yield 1; - } - - var g = gen(); - var exception = new Error("unhandled exception"); - try { - g.throw(exception); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, exception); - assert.strictEqual(began, false); - } - }); - - it("should not propagate errors handled inside a delegate", function() { - function *outer() { - try { - yield* inner(); - } catch (err) { - return -1; - } - return 1; - } - - function *inner() { - try { - yield void 0; - } catch (e) { - return; - } - } - - var g = outer(); - g.next(); - assert.equal(g.throw(new Error('foo')).value, 1); - }); - - it("should propagate errors unhandled inside a delegate", function() { - function *outer() { - try { - yield* inner(); - } catch (err) { - return -1; - } - return 1; - } - - function *inner() { - yield void 0; - } - - var g = outer(); - g.next(); - assert.equal(g.throw(new Error('foo')).value, -1); - }); -}); - -describe("unqualified function calls", function() { - it("should have a global `this` object", function() { - function getThis() { - return this; - } - - // This is almost certainly the global object, but there's a chance it - // might be null or undefined (in strict mode). - var unqualifiedThis = getThis(); - - function *invoke() { - // It seems like a bug in the ES6 spec that we have to yield an - // argument instead of just calling (yield)(). - return (yield "dummy")(); - } - - var g = invoke(); - var info = g.next(); - - assert.deepEqual(info, { value: "dummy", done: false }); - - info = g.next(getThis); - - // Avoid using assert.strictEqual when the arguments might equal the - // global object, since JSON.stringify chokes on circular structures. - assert.ok(info.value === unqualifiedThis); - - assert.strictEqual(info.done, true); - }); -}); - -describe("yield* expression results", function () { - it("have correct values", function () { - function* foo() { - yield 0; - return yield* bar(); - } - - function* bar() { - yield 1; - return 2; - } - - check(foo(), [0, 1], 2); - }); - - it("can be used in complex expressions", function () { - function pumpNumber(gen) { - var n = 0; - - while (true) { - var res = n > 0 ? gen.next(n) : gen.next(); - n = res.value; - if (res.done) { - return n; - } - } - } - - function* foo() { - return (yield* bar()) + (yield* bar()); - } - - function* bar() { - return (yield 2) + (yield 3); - } - - assert.strictEqual(pumpNumber(bar()), 5); - assert.strictEqual(pumpNumber(foo()), 10); - }); -}); - -describe("isGeneratorFunction", function() { - it("should work for function declarations", function() { - // Do the assertions up here to make sure the generator function is - // marked at the beginning of the block the function is declared in. - assert.strictEqual( - regeneratorRuntime.isGeneratorFunction(genFun), - true - ); - - assert.strictEqual( - regeneratorRuntime.isGeneratorFunction(normalFun), - false - ); - - function normalFun() { - return 0; - } - - function *genFun() { - yield 0; - } - }); - - it("should work for function expressions", function() { - assert.strictEqual( - regeneratorRuntime.isGeneratorFunction(function *genFun() { - yield 0; - }), - true - ); - - assert.strictEqual( - regeneratorRuntime.isGeneratorFunction(function normalFun() { - return 0; - }), - false - ); - }); -}); - -describe("new expressions", function() { - it("should be able to contain yield sub-expressions", function() { - function A(first, second) { - this.first = first; - this.second = second; - } - - function *gen() { - return yield new (yield 0)(yield 1, yield 2); - } - - var g = gen(); - - assert.deepEqual(g.next(), { value: 0, done: false }); - assert.deepEqual(g.next(A), { value: 1, done: false }); - assert.deepEqual(g.next("asdf"), { value: 2, done: false }); - - var info = g.next("zxcv"); - assert.strictEqual(info.done, false); - assert.ok(info.value instanceof A); - assert.strictEqual(info.value.first, "asdf"); - assert.strictEqual(info.value.second, "zxcv"); - - assert.deepEqual(g.next("qwer"), { value: "qwer", done: true }); - }); -}); - -describe("block binding", function() { - it("should translate block binding correctly", function() { - "use strict"; - - function *gen() { - var a$0 = 0, a$1 = 1; - - let a = 3; - - { - let a = 1; - yield a + a$0; - } - - { - let a = 2; - yield a - 1 + a$1; - } - - yield a; - } - - var g = gen(); - - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: 2, done: false }); - assert.deepEqual(g.next(), { value: 3, done: false }); - assert.deepEqual(g.next(), { value: void 0, done: true }); - }); - - it("should translate block binding with iife correctly", function() { - "use strict"; - - function *gen() { - let arr = []; - - for (let x = 0; x < 3; x++) { - let y = x; - arr.push(function() { return y; }); - } - - { - let x; - while( x = arr.pop() ) { - yield x; - } - } - } - - var g = gen(); - - assert.equal(g.next().value(), 2); - assert.equal(g.next().value(), 1); - assert.equal(g.next().value(), 0); - assert.deepEqual(g.next(), { value: void 0, done: true }); - }); -}); - -describe("newborn generators", function() { - it("should be able to yield* non-newborn generators", function() { - function *inner() { - return [yield 1, yield 2]; - } - - function *outer(delegate) { - return yield* delegate; - } - - var n = inner(); - - assert.deepEqual(n.next(), { - value: 1, - done: false - }); - - var g = outer(n); - - // I would really like to be able to pass 3 to g.next here, but V8 - // ignores values sent to newborn generators, and SpiderMonkey throws - // a TypeError. - assert.deepEqual(g.next(), { - value: 2, - done: false - }); - - assert.deepEqual(g.next(4), { - value: [void 0, 4], - done: true - }); - }); - - it("should support the ignore-initial-yield wrapper idiom", function() { - var markers = []; - - function *inner() { - markers.push(0); - var sent1 = yield 1; - markers.push(2); - var sent2 = yield 2; - markers.push(3); - return [sent1, sent2]; - } - - function wrapper(delegate) { - var gen = (function*() { - // This yield is the "initial yield" whose argument we ignore. - var sent = yield "ignored", info; - - markers.push(1); - - while (!(info = delegate.next(sent)).done) { - sent = yield info.value; - } - - markers.push(4); - - return info.value; - })(); - - // Ensure that gen is not newborn and that the next invocation of - // gen.next(value) can send value to the initial yield expression. - gen.next(); - - return gen; - } - - var n = inner(); - - assert.deepEqual(n.next(), { - value: 1, - done: false - }); - - var g = wrapper(n); - - // Unlike in the previous spec, it's fine to pass 3 to g.next here, - // because g is not newborn, because g.next was already called once - // before g was returned from the wrapper function. - assert.deepEqual(g.next(3), { - value: 2, - done: false - }); - - assert.deepEqual(g.next(4), { - value: [3, 4], - done: true - }); - - // Ensure we encountered the marker points in the expected order. - assert.deepEqual(markers, [0, 1, 2, 3, 4]); - }); - - it("should allow chaining newborn and non-newborn generators", function() { - function *range(n) { - for (var i = 0; i < n; ++i) { - yield i; - } - } - - function *chain(a, b) { - yield* a; - yield* b; - } - - check(chain(range(3), range(5)), [0, 1, 2, 0, 1, 2, 3, 4]); - - function *y3(x) { - return yield yield yield x; - } - - function *y5(x) { - return yield yield yield yield yield x; - } - - check( - chain(y3("foo"), y5("bar")), - ["foo", 1, 2, "bar", 4, 5, 6, 7] - ); - - var g3 = y3("three"); - assert.deepEqual(g3.next(), { - value: "three", - done: false - }); - - var g5 = y5("five"); - assert.deepEqual(g5.next(), { - value: "five", - done: false - }); - - var undef; // A little easier to read than void 0. - check(chain(g3, g5), [undef, 1, undef, 3, 4, 5]); - }); -}); - -describe("labeled break and continue statements", function() { - it("should be able to exit multiple try statements", function() { - var e1 = "first"; - var e2 = "second"; - var e3 = "third"; - var e4 = "fourth"; - - function *gen(n, which) { - try { - yield 0; - raise(e1); - - } finally { - yield 1; - - loop: - for (var i = 0; i < n; ++i) { - yield i; - - try { - raise(e2); - } finally { - yield 2; - - try { - raise(e3); - } finally { - yield 3; - - try { - raise(e4); - } finally { - yield 4; - - if (which === "break") { - yield "breaking"; - break loop; - } - - if (which === "continue") { - yield "continuing"; - continue loop; - } - - yield 5; - } - } - } - } - - yield 6; - } - } - - try { - check(gen(1, "break"), [ - 0, 1, 0, 2, 3, 4, "breaking", 6 - ]); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, e1); - } - - try { - check(gen(3, "continue"), [ - 0, 1, 0, 2, 3, 4, "continuing", - 1, 2, 3, 4, "continuing", - 2, 2, 3, 4, "continuing", - 6 // Loop finished naturally. - ]); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, e1); - } - - try { - check(gen(3, "neither"), [ - 0, 1, 0, 2, 3, 4, 5 - ]); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, e4); - } - }); - - it("should allow breaking from any labeled statement", function() { - function* gen(limit) { - yield 0; - - for (var i = 0; i < limit; ++i) { - yield 1; - - label1: { - yield 2; - break label1; - yield 3; - } - - label2: - if (limit === 3) label3: { - yield 4; - if (i === 0) break label2; - yield 5; - if (i === 1) break label3; - label4: yield 6; - // This should break from the for-loop. - if (i === 2) xxx: break; - yield 7; - } - - // This should be a no-op. - xxx: break xxx; - - yield 8 - } - - yield 9; - } - - check(gen(0), [0, 9]); - check(gen(1), [0, 1, 2, 8, 9]); - check(gen(2), [0, 1, 2, 8, 1, 2, 8, 9]); - check(gen(3), [0, 1, 2, 4, 8, 1, 2, 4, 5, 8, 1, 2, 4, 5, 6, 9]); - }); -}); - -describe("for loop with var decl and no update expression", function() { - // https://github.com/facebook/regenerator/issues/103 - function *range() { - for (var i = 0; false; ) { - } - } - - it("should compile and run", function() { - check(range(), []); - }); -}); - -describe("generator function prototype", function() { - function getProto(obj) { - return Object.getPrototypeOf - ? Object.getPrototypeOf(obj) - : obj.__proto__; - } - - it("should follow the expected object model", function() { - var GeneratorFunctionPrototype = getProto(f); - var GeneratorFunction = GeneratorFunctionPrototype.constructor; - - assert.strictEqual(GeneratorFunction.name, 'GeneratorFunction'); - assert.strictEqual(GeneratorFunction.prototype, - GeneratorFunctionPrototype); - assert.strictEqual(GeneratorFunctionPrototype.prototype.constructor, - GeneratorFunctionPrototype); - assert.strictEqual(GeneratorFunctionPrototype.prototype, - getProto(f.prototype)); - assert.strictEqual(getProto(GeneratorFunctionPrototype), - Function.prototype); - - if (typeof process === "undefined" || - process.version.slice(1, 3) === "0.") { - // Node version strings start with 0. - assert.strictEqual(GeneratorFunctionPrototype.name, - "GeneratorFunctionPrototype"); - } else if (process.version.slice(1, 3) === "1.") { - // iojs version strings start with 1., and iojs gets this .name - // property wrong. TODO report this? - assert.strictEqual(GeneratorFunctionPrototype.name, ""); - } - - assert.strictEqual(typeof f2, "function"); - assert.strictEqual(f2.constructor, GeneratorFunction); - assert.ok(f2 instanceof GeneratorFunction); - assert.strictEqual(f2.name, "f2"); - - var g = f(); - assert.ok(g instanceof f); - assert.strictEqual(getProto(g), f.prototype); - - assert.deepEqual([], Object.getOwnPropertyNames(f.prototype)); - // assert.deepEqual([], Object.getOwnPropertyNames(g)); - - f.prototype.x = 42; - - var g2 = f(); - assert.strictEqual(g2.x, 42); - - var g3 = new f(); - assert.strictEqual(g3.x, 42); - - function* f2() { - yield 1; - } - - assert.strictEqual(getProto(f), getProto(f2)); - assert.strictEqual(f.hasOwnProperty('constructor'), false); - assert.strictEqual(getProto(f).constructor.name, 'GeneratorFunction'); - - // Intentionally at the end to test hoisting. - function* f() { - yield this; - } - - function* f() { - yield 1; - } - - var f2 = f; - f = 42; - var g = f2(); - - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: void 0, done: true }); - assert.ok(g instanceof f2); - }); -}); - -describe("for-of loops", function() { - (runningInTranslation ? it : xit) - ("should work for Arrays", function() { - var sum = 0; - for (var x of [1, 2].concat(3)) { - sum += x; - } - assert.strictEqual(sum, 6); - }); - - it("should work for generators", function() { - var value, values = []; - for (value of range(3)) - values.push(value); - assert.deepEqual(values, [0, 1, 2]); - }); - - it("should work inside of generators", function() { - function *yieldPermutations(list) { - if (list.length < 2) { - yield list; - return 1; - } - - var count = 0; - var first = list.slice(0, 1); - var genRest = yieldPermutations(list.slice(1)); - - for (var perm of genRest) { - for (var i = 0; i < list.length; ++i) { - var prefix = perm.slice(0, i); - var suffix = perm.slice(i); - yield prefix.concat(first, suffix); - } - - count += i; - } - - return count; - } - - var count = 0; - for (var perm of yieldPermutations([])) { - assert.deepEqual(perm, []); - ++count; - } - assert.strictEqual(count, 1); - - check(yieldPermutations([1]), [[1]], 1); - - check(yieldPermutations([2, 1]), [ - [2, 1], - [1, 2] - ], 2); - - check(yieldPermutations([1,3,2]), [ - [1, 3, 2], - [3, 1, 2], - [3, 2, 1], - [1, 2, 3], - [2, 1, 3], - [2, 3, 1] - ], 6); - }); -}); - -describe("generator return method", function() { - if (!runningInTranslation) { - // The return method has not been specified or implemented natively, - // yet, so these tests need only pass in translation. - return; - } - - it("should work with newborn generators", function() { - function *gen() { - yield 0; - } - - var g = gen(); - - assert.deepEqual(g.return("argument"), { - value: "argument", - done: true - }); - - assertAlreadyFinished(g); - }); - - it("should behave as if generator actually returned", function() { - var executedFinally = false; - - function *gen() { - try { - yield 0; - } catch (err) { - assert.ok(false, "should not have executed the catch handler"); - } finally { - executedFinally = true; - } - } - - var g = gen(); - assert.deepEqual(g.next(), { value: 0, done: false }); - - assert.deepEqual(g.return("argument"), { - value: "argument", - done: true - }); - - assert.strictEqual(executedFinally, true); - assertAlreadyFinished(g); - }); - - it("should return both delegate and delegator", function() { - var checkpoints = []; - - function* callee(errorToThrow) { - try { - yield 1; - yield 2; - } finally { - checkpoints.push("callee finally"); - if (errorToThrow) { - throw errorToThrow; - } - } - } - - function* caller(errorToThrow) { - try { - yield 0; - yield* callee(errorToThrow); - yield 3; - } finally { - checkpoints.push("caller finally"); - } - } - - var g1 = caller(); - - assert.deepEqual(g1.next(), { value: 0, done: false }); - assert.deepEqual(g1.next(), { value: 1, done: false }); - assert.deepEqual(g1.return(-1), { value: -1, done: true }); - assert.deepEqual(checkpoints, [ - "callee finally", - "caller finally" - ]); - - var error = new Error("thrown from callee"); - var g2 = caller(error); - - assert.deepEqual(g2.next(), { value: 0, done: false }); - assert.deepEqual(g2.next(), { value: 1, done: false }); - - try { - g2.return(-1); - assert.ok(false, "should have thrown an exception"); - } catch (thrown) { - assert.strictEqual(thrown, error); - } - - assert.deepEqual(checkpoints, [ - "callee finally", - "caller finally", - "callee finally", - "caller finally" - ]); - }); -}); - -describe("expressions containing yield subexpressions", function() { - it("should evaluate all subexpressions before yielding", function() { - function *gen(x) { - return x * (yield (function(y) { x = y })); - } - - var g = gen(2); - var result = g.next(); - assert.strictEqual(result.done, false); - - result.value(5); - - assert.deepEqual(g.next(5), { - value: 10, - done: true - }); - }); - - it("should work even with getter member expressions", function() { - function *gen() { - return a.b + (yield "asdf"); - } - - var a = {}; - var b = 0; - - Object.defineProperty(a, "b", { - get: function() { - return ++b; - } - }); - - var g = gen(); - - assert.strictEqual(a.b, 1); - - assert.deepEqual(g.next(), { - value: "asdf", - done: false - }); - - assert.strictEqual(a.b, 3); - - assert.deepEqual(g.next(2), { - value: 4, - done: true - }); - }); - - it("should evaluate all array elements before yielding", function() { - function *gen() { - return [a, yield "asdf", a]; - } - - var a = 1; - var g = gen(); - - assert.deepEqual(g.next(), { - value: "asdf", - done: false - }); - - a = 3; - - assert.deepEqual(g.next(2), { - value: [1, 2, 3], - done: true - }); - }); - - it("should handle callee member expressions correctly", function() { - function *gen() { - a = a.slice(0).concat(yield "asdf"); - return a; - } - - var a = []; - var g = gen(); - - assert.deepEqual(g.next(), { - value: "asdf", - done: false - }); - - a.push(1); - - assert.deepEqual(g.next(2), { - value: [2], - done: true - }); - }); - - it("should handle implicit stringification correctly", function() { - function *gen() { - return a + (yield "asdf"); - } - - var a = [1, 2]; - var g = gen(); - - assert.deepEqual(g.next(), { - value: "asdf", - done: false - }); - - a = [4,5]; - - assert.deepEqual(g.next(",3"), { - value: "1,2,3", - done: true - }); - }); -}); diff --git a/packages/babel-plugin-transform-regenerator/package.json b/packages/babel-plugin-transform-regenerator/package.json index 491b227a0fd64..e884de7694c25 100644 --- a/packages/babel-plugin-transform-regenerator/package.json +++ b/packages/babel-plugin-transform-regenerator/package.json @@ -7,9 +7,7 @@ "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator", "main": "lib/index.js", "dependencies": { - "babel-types": "^6.16.0", - "babel-runtime": "^6.9.0", - "private": "~0.1.5" + "regenerator-transform": "0.9.8" }, "license": "BSD", "devDependencies": { diff --git a/packages/babel-plugin-transform-regenerator/src/emit.js b/packages/babel-plugin-transform-regenerator/src/emit.js deleted file mode 100644 index b3378867b3b41..0000000000000 --- a/packages/babel-plugin-transform-regenerator/src/emit.js +++ /dev/null @@ -1,1184 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import assert from "assert"; -import * as t from "babel-types"; -import * as leap from "./leap"; -import * as meta from "./meta"; -import * as util from "./util"; - -let hasOwn = Object.prototype.hasOwnProperty; - -function Emitter(contextId) { - assert.ok(this instanceof Emitter); - t.assertIdentifier(contextId); - - // Used to generate unique temporary names. - this.nextTempId = 0; - - // In order to make sure the context object does not collide with - // anything in the local scope, we might have to rename it, so we - // refer to it symbolically instead of just assuming that it will be - // called "context". - this.contextId = contextId; - - // An append-only list of Statements that grows each time this.emit is - // called. - this.listing = []; - - // A sparse array whose keys correspond to locations in this.listing - // that have been marked as branch/jump targets. - this.marked = [true]; - - // The last location will be marked when this.getDispatchLoop is - // called. - this.finalLoc = loc(); - - // A list of all leap.TryEntry statements emitted. - this.tryEntries = []; - - // Each time we evaluate the body of a loop, we tell this.leapManager - // to enter a nested loop context that determines the meaning of break - // and continue statements therein. - this.leapManager = new leap.LeapManager(this); -} - -let Ep = Emitter.prototype; -exports.Emitter = Emitter; - -// Offsets into this.listing that could be used as targets for branches or -// jumps are represented as numeric Literal nodes. This representation has -// the amazingly convenient benefit of allowing the exact value of the -// location to be determined at any time, even after generating code that -// refers to the location. -function loc() { - return t.numericLiteral(-1); -} - -// Sets the exact value of the given location to the offset of the next -// Statement emitted. -Ep.mark = function(loc) { - t.assertLiteral(loc); - let index = this.listing.length; - if (loc.value === -1) { - loc.value = index; - } else { - // Locations can be marked redundantly, but their values cannot change - // once set the first time. - assert.strictEqual(loc.value, index); - } - this.marked[index] = true; - return loc; -}; - -Ep.emit = function(node) { - if (t.isExpression(node)) { - node = t.expressionStatement(node); - } - - t.assertStatement(node); - this.listing.push(node); -}; - -// Shorthand for emitting assignment statements. This will come in handy -// for assignments to temporary variables. -Ep.emitAssign = function(lhs, rhs) { - this.emit(this.assign(lhs, rhs)); - return lhs; -}; - -// Shorthand for an assignment statement. -Ep.assign = function(lhs, rhs) { - return t.expressionStatement( - t.assignmentExpression("=", lhs, rhs)); -}; - -// Convenience function for generating expressions like context.next, -// context.sent, and context.rval. -Ep.contextProperty = function(name, computed) { - return t.memberExpression( - this.contextId, - computed ? t.stringLiteral(name) : t.identifier(name), - !!computed - ); -}; - -// Shorthand for setting context.rval and jumping to `context.stop()`. -Ep.stop = function(rval) { - if (rval) { - this.setReturnValue(rval); - } - - this.jump(this.finalLoc); -}; - -Ep.setReturnValue = function(valuePath) { - t.assertExpression(valuePath.value); - - this.emitAssign( - this.contextProperty("rval"), - this.explodeExpression(valuePath) - ); -}; - -Ep.clearPendingException = function(tryLoc, assignee) { - t.assertLiteral(tryLoc); - - let catchCall = t.callExpression( - this.contextProperty("catch", true), - [tryLoc] - ); - - if (assignee) { - this.emitAssign(assignee, catchCall); - } else { - this.emit(catchCall); - } -}; - -// Emits code for an unconditional jump to the given location, even if the -// exact value of the location is not yet known. -Ep.jump = function(toLoc) { - this.emitAssign(this.contextProperty("next"), toLoc); - this.emit(t.breakStatement()); -}; - -// Conditional jump. -Ep.jumpIf = function(test, toLoc) { - t.assertExpression(test); - t.assertLiteral(toLoc); - - this.emit(t.ifStatement( - test, - t.blockStatement([ - this.assign(this.contextProperty("next"), toLoc), - t.breakStatement() - ]) - )); -}; - -// Conditional jump, with the condition negated. -Ep.jumpIfNot = function(test, toLoc) { - t.assertExpression(test); - t.assertLiteral(toLoc); - - let negatedTest; - if (t.isUnaryExpression(test) && - test.operator === "!") { - // Avoid double negation. - negatedTest = test.argument; - } else { - negatedTest = t.unaryExpression("!", test); - } - - this.emit(t.ifStatement( - negatedTest, - t.blockStatement([ - this.assign(this.contextProperty("next"), toLoc), - t.breakStatement() - ]) - )); -}; - -// Returns a unique MemberExpression that can be used to store and -// retrieve temporary values. Since the object of the member expression is -// the context object, which is presumed to coexist peacefully with all -// other local variables, and since we just increment `nextTempId` -// monotonically, uniqueness is assured. -Ep.makeTempVar = function() { - return this.contextProperty("t" + this.nextTempId++); -}; - -Ep.getContextFunction = function(id) { - return t.functionExpression( - id || null/*Anonymous*/, - [this.contextId], - t.blockStatement([this.getDispatchLoop()]), - false, // Not a generator anymore! - false // Nor an expression. - ); -}; - -// Turns this.listing into a loop of the form -// -// while (1) switch (context.next) { -// case 0: -// ... -// case n: -// return context.stop(); -// } -// -// Each marked location in this.listing will correspond to one generated -// case statement. -Ep.getDispatchLoop = function() { - let self = this; - let cases = []; - let current; - - // 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. - let alreadyEnded = false; - - self.listing.forEach(function(stmt, i) { - if (self.marked.hasOwnProperty(i)) { - cases.push(t.switchCase( - t.numericLiteral(i), - current = [])); - alreadyEnded = false; - } - - if (!alreadyEnded) { - current.push(stmt); - if (t.isCompletionStatement(stmt)) - alreadyEnded = true; - } - }); - - // Now that we know how many statements there will be in this.listing, - // we can finally resolve this.finalLoc.value. - this.finalLoc.value = this.listing.length; - - cases.push( - t.switchCase(this.finalLoc, [ - // Intentionally fall through to the "end" case... - ]), - - // So that the runtime can jump to the final location without having - // to know its offset, we provide the "end" case as a synonym. - t.switchCase(t.stringLiteral("end"), [ - // This will check/clear both context.thrown and context.rval. - t.returnStatement( - t.callExpression(this.contextProperty("stop"), []) - ) - ]) - ); - - return t.whileStatement( - t.numericLiteral(1), - t.switchStatement( - t.assignmentExpression( - "=", - this.contextProperty("prev"), - this.contextProperty("next") - ), - cases - ) - ); -}; - -Ep.getTryLocsList = function() { - if (this.tryEntries.length === 0) { - // To avoid adding a needless [] to the majority of runtime.wrap - // argument lists, force the caller to handle this case specially. - return null; - } - - let lastLocValue = 0; - - return t.arrayExpression( - this.tryEntries.map(function(tryEntry) { - let thisLocValue = tryEntry.firstLoc.value; - assert.ok(thisLocValue >= lastLocValue, "try entries out of order"); - lastLocValue = thisLocValue; - - let ce = tryEntry.catchEntry; - let fe = tryEntry.finallyEntry; - - let locs = [ - tryEntry.firstLoc, - // The null here makes a hole in the array. - ce ? ce.firstLoc : null - ]; - - if (fe) { - locs[2] = fe.firstLoc; - locs[3] = fe.afterLoc; - } - - return t.arrayExpression(locs); - }) - ); -}; - -// All side effects must be realized in order. - -// If any subexpression harbors a leap, all subexpressions must be -// neutered of side effects. - -// No destructive modification of AST nodes. - -Ep.explode = function(path, ignoreResult) { - let node = path.node; - let self = this; - - t.assertNode(node); - - if (t.isDeclaration(node)) - throw getDeclError(node); - - if (t.isStatement(node)) - return self.explodeStatement(path); - - if (t.isExpression(node)) - return self.explodeExpression(path, ignoreResult); - - switch (node.type) { - case "Program": - return path.get("body").map( - self.explodeStatement, - self - ); - - case "VariableDeclarator": - throw getDeclError(node); - - // These node types should be handled by their parent nodes - // (ObjectExpression, SwitchStatement, and TryStatement, respectively). - case "Property": - case "SwitchCase": - case "CatchClause": - throw new Error( - node.type + " nodes should be handled by their parents"); - - default: - throw new Error( - "unknown Node of type " + - JSON.stringify(node.type)); - } -}; - -function getDeclError(node) { - return new Error( - "all declarations should have been transformed into " + - "assignments before the Exploder began its work: " + - JSON.stringify(node)); -} - -Ep.explodeStatement = function(path, labelId) { - let stmt = path.node; - let self = this; - let before, after, head; - - t.assertStatement(stmt); - - if (labelId) { - t.assertIdentifier(labelId); - } else { - labelId = null; - } - - // Explode BlockStatement nodes even if they do not contain a yield, - // because we don't want or need the curly braces. - if (t.isBlockStatement(stmt)) { - path.get("body").forEach(function (path) { - self.explodeStatement(path); - }); - return; - } - - if (!meta.containsLeap(stmt)) { - // Technically we should be able to avoid emitting the statement - // altogether if !meta.hasSideEffects(stmt), but that leads to - // confusing generated code (for instance, `while (true) {}` just - // disappears) and is probably a more appropriate job for a dedicated - // dead code elimination pass. - self.emit(stmt); - return; - } - - switch (stmt.type) { - case "ExpressionStatement": - self.explodeExpression(path.get("expression"), true); - break; - - case "LabeledStatement": - after = loc(); - - // Did you know you can break from any labeled block statement or - // control structure? Well, you can! Note: when a labeled loop is - // encountered, the leap.LabeledEntry created here will immediately - // enclose a leap.LoopEntry on the leap manager's stack, and both - // entries will have the same label. Though this works just fine, it - // may seem a bit redundant. In theory, we could check here to - // determine if stmt knows how to handle its own label; for example, - // stmt happens to be a WhileStatement and so we know it's going to - // establish its own LoopEntry when we explode it (below). Then this - // LabeledEntry would be unnecessary. Alternatively, we might be - // tempted not to pass stmt.label down into self.explodeStatement, - // because we've handled the label here, but that's a mistake because - // labeled loops may contain labeled continue statements, which is not - // something we can handle in this generic case. All in all, I think a - // little redundancy greatly simplifies the logic of this case, since - // it's clear that we handle all possible LabeledStatements correctly - // here, regardless of whether they interact with the leap manager - // themselves. Also remember that labels and break/continue-to-label - // statements are rare, and all of this logic happens at transform - // time, so it has no additional runtime cost. - self.leapManager.withEntry( - new leap.LabeledEntry(after, stmt.label), - function() { - self.explodeStatement(path.get("body"), stmt.label); - } - ); - - self.mark(after); - - break; - - case "WhileStatement": - before = loc(); - after = loc(); - - self.mark(before); - self.jumpIfNot(self.explodeExpression(path.get("test")), after); - self.leapManager.withEntry( - new leap.LoopEntry(after, before, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - self.jump(before); - self.mark(after); - - break; - - case "DoWhileStatement": - let first = loc(); - let test = loc(); - after = loc(); - - self.mark(first); - self.leapManager.withEntry( - new leap.LoopEntry(after, test, labelId), - function() { self.explode(path.get("body")); } - ); - self.mark(test); - self.jumpIf(self.explodeExpression(path.get("test")), first); - self.mark(after); - - break; - - case "ForStatement": - head = loc(); - let update = loc(); - after = loc(); - - if (stmt.init) { - // We pass true here to indicate that if stmt.init is an expression - // then we do not care about its result. - self.explode(path.get("init"), true); - } - - self.mark(head); - - if (stmt.test) { - self.jumpIfNot(self.explodeExpression(path.get("test")), after); - } else { - // No test means continue unconditionally. - } - - self.leapManager.withEntry( - new leap.LoopEntry(after, update, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - - self.mark(update); - - if (stmt.update) { - // We pass true here to indicate that if stmt.update is an - // expression then we do not care about its result. - self.explode(path.get("update"), true); - } - - self.jump(head); - - self.mark(after); - - break; - - case "TypeCastExpression": - return self.explodeExpression(path.get("expression")); - - case "ForInStatement": - head = loc(); - after = loc(); - - let keyIterNextFn = self.makeTempVar(); - self.emitAssign( - keyIterNextFn, - t.callExpression( - util.runtimeProperty("keys"), - [self.explodeExpression(path.get("right"))] - ) - ); - - self.mark(head); - - let keyInfoTmpVar = self.makeTempVar(); - self.jumpIf( - t.memberExpression( - t.assignmentExpression( - "=", - keyInfoTmpVar, - t.callExpression(keyIterNextFn, []) - ), - t.identifier("done"), - false - ), - after - ); - - self.emitAssign( - stmt.left, - t.memberExpression( - keyInfoTmpVar, - t.identifier("value"), - false - ) - ); - - self.leapManager.withEntry( - new leap.LoopEntry(after, head, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - - self.jump(head); - - self.mark(after); - - break; - - case "BreakStatement": - self.emitAbruptCompletion({ - type: "break", - target: self.leapManager.getBreakLoc(stmt.label) - }); - - break; - - case "ContinueStatement": - self.emitAbruptCompletion({ - type: "continue", - target: self.leapManager.getContinueLoc(stmt.label) - }); - - break; - - case "SwitchStatement": - // Always save the discriminant into a temporary variable in case the - // test expressions overwrite values like context.sent. - let disc = self.emitAssign( - self.makeTempVar(), - self.explodeExpression(path.get("discriminant")) - ); - - after = loc(); - let defaultLoc = loc(); - let condition = defaultLoc; - let caseLocs = []; - - // If there are no cases, .cases might be undefined. - let cases = stmt.cases || []; - - for (let i = cases.length - 1; i >= 0; --i) { - let c = cases[i]; - t.assertSwitchCase(c); - - if (c.test) { - condition = t.conditionalExpression( - t.binaryExpression("===", disc, c.test), - caseLocs[i] = loc(), - condition - ); - } else { - caseLocs[i] = defaultLoc; - } - } - - let discriminant = path.get("discriminant"); - discriminant.replaceWith(condition); - self.jump(self.explodeExpression(discriminant)); - - self.leapManager.withEntry( - new leap.SwitchEntry(after), - function() { - path.get("cases").forEach(function(casePath) { - let i = casePath.key; - self.mark(caseLocs[i]); - - casePath.get("consequent").forEach(function (path) { - self.explodeStatement(path); - }); - }); - } - ); - - self.mark(after); - if (defaultLoc.value === -1) { - self.mark(defaultLoc); - assert.strictEqual(after.value, defaultLoc.value); - } - - break; - - case "IfStatement": - let elseLoc = stmt.alternate && loc(); - after = loc(); - - self.jumpIfNot( - self.explodeExpression(path.get("test")), - elseLoc || after - ); - - self.explodeStatement(path.get("consequent")); - - if (elseLoc) { - self.jump(after); - self.mark(elseLoc); - self.explodeStatement(path.get("alternate")); - } - - self.mark(after); - - break; - - case "ReturnStatement": - self.emitAbruptCompletion({ - type: "return", - value: self.explodeExpression(path.get("argument")) - }); - - break; - - case "WithStatement": - throw new Error("WithStatement not supported in generator functions."); - - case "TryStatement": - after = loc(); - - let handler = stmt.handler; - - let catchLoc = handler && loc(); - let catchEntry = catchLoc && new leap.CatchEntry( - catchLoc, - handler.param - ); - - let finallyLoc = stmt.finalizer && loc(); - let finallyEntry = finallyLoc && - new leap.FinallyEntry(finallyLoc, after); - - let tryEntry = new leap.TryEntry( - self.getUnmarkedCurrentLoc(), - catchEntry, - finallyEntry - ); - - self.tryEntries.push(tryEntry); - self.updateContextPrevLoc(tryEntry.firstLoc); - - self.leapManager.withEntry(tryEntry, function() { - self.explodeStatement(path.get("block")); - - if (catchLoc) { - if (finallyLoc) { - // If we have both a catch block and a finally block, then - // because we emit the catch block first, we need to jump over - // it to the finally block. - self.jump(finallyLoc); - - } else { - // If there is no finally block, then we need to jump over the - // catch block to the fall-through location. - self.jump(after); - } - - self.updateContextPrevLoc(self.mark(catchLoc)); - - let bodyPath = path.get("handler.body"); - let safeParam = self.makeTempVar(); - self.clearPendingException(tryEntry.firstLoc, safeParam); - - bodyPath.traverse(catchParamVisitor, { - safeParam: safeParam, - catchParamName: handler.param.name - }); - - self.leapManager.withEntry(catchEntry, function() { - self.explodeStatement(bodyPath); - }); - } - - if (finallyLoc) { - self.updateContextPrevLoc(self.mark(finallyLoc)); - - self.leapManager.withEntry(finallyEntry, function() { - self.explodeStatement(path.get("finalizer")); - }); - - self.emit(t.returnStatement(t.callExpression( - self.contextProperty("finish"), - [finallyEntry.firstLoc] - ))); - } - }); - - self.mark(after); - - break; - - case "ThrowStatement": - self.emit(t.throwStatement( - self.explodeExpression(path.get("argument")) - )); - - break; - - default: - throw new Error( - "unknown Statement of type " + - JSON.stringify(stmt.type)); - } -}; - -let catchParamVisitor = { - Identifier: function(path, state) { - if (path.node.name === state.catchParamName && util.isReference(path)) { - path.replaceWith(state.safeParam); - } - }, - - Scope: function(path, state) { - if (path.scope.hasOwnBinding(state.catchParamName)) { - // Don't descend into nested scopes that shadow the catch - // parameter with their own declarations. - path.skip(); - } - } -}; - -Ep.emitAbruptCompletion = function(record) { - if (!isValidCompletion(record)) { - assert.ok( - false, - "invalid completion record: " + - JSON.stringify(record) - ); - } - - assert.notStrictEqual( - record.type, "normal", - "normal completions are not abrupt" - ); - - let abruptArgs = [t.stringLiteral(record.type)]; - - if (record.type === "break" || - record.type === "continue") { - t.assertLiteral(record.target); - abruptArgs[1] = record.target; - } else if (record.type === "return" || - record.type === "throw") { - if (record.value) { - t.assertExpression(record.value); - abruptArgs[1] = record.value; - } - } - - this.emit( - t.returnStatement( - t.callExpression( - this.contextProperty("abrupt"), - abruptArgs - ) - ) - ); -}; - -function isValidCompletion(record) { - let type = record.type; - - if (type === "normal") { - return !hasOwn.call(record, "target"); - } - - if (type === "break" || - type === "continue") { - return !hasOwn.call(record, "value") - && t.isLiteral(record.target); - } - - if (type === "return" || - type === "throw") { - return hasOwn.call(record, "value") - && !hasOwn.call(record, "target"); - } - - return false; -} - - -// Not all offsets into emitter.listing are potential jump targets. For -// example, execution typically falls into the beginning of a try block -// without jumping directly there. This method returns the current offset -// without marking it, so that a switch case will not necessarily be -// generated for this offset (I say "not necessarily" because the same -// location might end up being marked in the process of emitting other -// statements). There's no logical harm in marking such locations as jump -// targets, but minimizing the number of switch cases keeps the generated -// code shorter. -Ep.getUnmarkedCurrentLoc = function() { - return t.numericLiteral(this.listing.length); -}; - -// The context.prev property takes the value of context.next whenever we -// evaluate the switch statement discriminant, which is generally good -// enough for tracking the last location we jumped to, but sometimes -// context.prev needs to be more precise, such as when we fall -// successfully out of a try block and into a finally block without -// jumping. This method exists to update context.prev to the freshest -// available location. If we were implementing a full interpreter, we -// would know the location of the current instruction with complete -// precision at all times, but we don't have that luxury here, as it would -// be costly and verbose to set context.prev before every statement. -Ep.updateContextPrevLoc = function(loc) { - if (loc) { - t.assertLiteral(loc); - - if (loc.value === -1) { - // If an uninitialized location literal was passed in, set its value - // to the current this.listing.length. - loc.value = this.listing.length; - } else { - // Otherwise assert that the location matches the current offset. - assert.strictEqual(loc.value, this.listing.length); - } - - } else { - loc = this.getUnmarkedCurrentLoc(); - } - - // Make sure context.prev is up to date in case we fell into this try - // statement without jumping to it. TODO Consider avoiding this - // assignment when we know control must have jumped here. - this.emitAssign(this.contextProperty("prev"), loc); -}; - -Ep.explodeExpression = function(path, ignoreResult) { - let expr = path.node; - if (expr) { - t.assertExpression(expr); - } else { - return expr; - } - - let self = this; - let result; // Used optionally by several cases below. - let after; - - function finish(expr) { - t.assertExpression(expr); - if (ignoreResult) { - self.emit(expr); - } else { - return expr; - } - } - - // If the expression does not contain a leap, then we either emit the - // expression as a standalone statement or return it whole. - if (!meta.containsLeap(expr)) { - return finish(expr); - } - - // If any child contains a leap (such as a yield or labeled continue or - // break statement), then any sibling subexpressions will almost - // certainly have to be exploded in order to maintain the order of their - // side effects relative to the leaping child(ren). - let hasLeapingChildren = meta.containsLeap.onlyChildren(expr); - - // In order to save the rest of explodeExpression from a combinatorial - // trainwreck of special cases, explodeViaTempVar is responsible for - // deciding when a subexpression needs to be "exploded," which is my - // very technical term for emitting the subexpression as an assignment - // to a temporary variable and the substituting the temporary variable - // for the original subexpression. Think of exploded view diagrams, not - // Michael Bay movies. The point of exploding subexpressions is to - // control the precise order in which the generated code realizes the - // side effects of those subexpressions. - function explodeViaTempVar(tempVar, childPath, ignoreChildResult) { - assert.ok( - !ignoreChildResult || !tempVar, - "Ignoring the result of a child expression but forcing it to " + - "be assigned to a temporary variable?" - ); - - let result = self.explodeExpression(childPath, ignoreChildResult); - - if (ignoreChildResult) { - // Side effects already emitted above. - - } else if (tempVar || (hasLeapingChildren && - !t.isLiteral(result))) { - // If tempVar was provided, then the result will always be assigned - // to it, even if the result does not otherwise need to be assigned - // to a temporary variable. When no tempVar is provided, we have - // the flexibility to decide whether a temporary variable is really - // necessary. Unfortunately, in general, a temporary variable is - // required whenever any child contains a yield expression, since it - // is difficult to prove (at all, let alone efficiently) whether - // this result would evaluate to the same value before and after the - // yield (see #206). One narrow case where we can prove it doesn't - // matter (and thus we do not need a temporary variable) is when the - // result in question is a Literal value. - result = self.emitAssign( - tempVar || self.makeTempVar(), - result - ); - } - return result; - } - - // If ignoreResult is true, then we must take full responsibility for - // emitting the expression with all its side effects, and we should not - // return a result. - - switch (expr.type) { - case "MemberExpression": - return finish(t.memberExpression( - self.explodeExpression(path.get("object")), - expr.computed - ? explodeViaTempVar(null, path.get("property")) - : expr.property, - expr.computed - )); - - case "CallExpression": - let calleePath = path.get("callee"); - let argsPath = path.get("arguments"); - - let newCallee; - let newArgs = []; - - let hasLeapingArgs = false; - argsPath.forEach(function(argPath) { - hasLeapingArgs = hasLeapingArgs || - meta.containsLeap(argPath.node); - }); - - if (t.isMemberExpression(calleePath.node)) { - if (hasLeapingArgs) { - // If the arguments of the CallExpression contained any yield - // expressions, then we need to be sure to evaluate the callee - // before evaluating the arguments, but if the callee was a member - // expression, then we must be careful that the object of the - // member expression still gets bound to `this` for the call. - - let newObject = explodeViaTempVar( - // Assign the exploded callee.object expression to a temporary - // variable so that we can use it twice without reevaluating it. - self.makeTempVar(), - calleePath.get("object") - ); - - let newProperty = calleePath.node.computed - ? explodeViaTempVar(null, calleePath.get("property")) - : calleePath.node.property; - - newArgs.unshift(newObject); - - newCallee = t.memberExpression( - t.memberExpression( - newObject, - newProperty, - calleePath.node.computed - ), - t.identifier("call"), - false - ); - - } else { - newCallee = self.explodeExpression(calleePath); - } - - } else { - newCallee = self.explodeExpression(calleePath); - - if (t.isMemberExpression(newCallee)) { - // If the callee was not previously a MemberExpression, then the - // CallExpression was "unqualified," meaning its `this` object - // should be the global object. If the exploded expression has - // become a MemberExpression (e.g. a context property, probably a - // temporary variable), then we need to force it to be unqualified - // by using the (0, object.property)(...) trick; otherwise, it - // will receive the object of the MemberExpression as its `this` - // object. - newCallee = t.sequenceExpression([ - t.numericLiteral(0), - newCallee - ]); - } - } - - argsPath.forEach(function(argPath) { - newArgs.push(explodeViaTempVar(null, argPath)); - }); - - return finish(t.callExpression( - newCallee, - newArgs - )); - - case "NewExpression": - return finish(t.newExpression( - explodeViaTempVar(null, path.get("callee")), - path.get("arguments").map(function(argPath) { - return explodeViaTempVar(null, argPath); - }) - )); - - case "ObjectExpression": - return finish(t.objectExpression( - path.get("properties").map(function(propPath) { - if (propPath.isObjectProperty()) { - return t.objectProperty( - propPath.node.key, - explodeViaTempVar(null, propPath.get("value")), - propPath.node.computed - ); - } else { - return propPath.node; - } - }) - )); - - case "ArrayExpression": - return finish(t.arrayExpression( - path.get("elements").map(function(elemPath) { - return explodeViaTempVar(null, elemPath); - }) - )); - - case "SequenceExpression": - let lastIndex = expr.expressions.length - 1; - - path.get("expressions").forEach(function(exprPath) { - if (exprPath.key === lastIndex) { - result = self.explodeExpression(exprPath, ignoreResult); - } else { - self.explodeExpression(exprPath, true); - } - }); - - return result; - - case "LogicalExpression": - after = loc(); - - if (!ignoreResult) { - result = self.makeTempVar(); - } - - let left = explodeViaTempVar(result, path.get("left")); - - if (expr.operator === "&&") { - self.jumpIfNot(left, after); - } else { - assert.strictEqual(expr.operator, "||"); - self.jumpIf(left, after); - } - - explodeViaTempVar(result, path.get("right"), ignoreResult); - - self.mark(after); - - return result; - - case "ConditionalExpression": - let elseLoc = loc(); - after = loc(); - let test = self.explodeExpression(path.get("test")); - - self.jumpIfNot(test, elseLoc); - - if (!ignoreResult) { - result = self.makeTempVar(); - } - - explodeViaTempVar(result, path.get("consequent"), ignoreResult); - self.jump(after); - - self.mark(elseLoc); - explodeViaTempVar(result, path.get("alternate"), ignoreResult); - - self.mark(after); - - return result; - - case "UnaryExpression": - return finish(t.unaryExpression( - expr.operator, - // Can't (and don't need to) break up the syntax of the argument. - // Think about delete a[b]. - self.explodeExpression(path.get("argument")), - !!expr.prefix - )); - - case "BinaryExpression": - return finish(t.binaryExpression( - expr.operator, - explodeViaTempVar(null, path.get("left")), - explodeViaTempVar(null, path.get("right")) - )); - - case "AssignmentExpression": - return finish(t.assignmentExpression( - expr.operator, - self.explodeExpression(path.get("left")), - self.explodeExpression(path.get("right")) - )); - - case "UpdateExpression": - return finish(t.updateExpression( - expr.operator, - self.explodeExpression(path.get("argument")), - expr.prefix - )); - - case "YieldExpression": - after = loc(); - let arg = expr.argument && self.explodeExpression(path.get("argument")); - - if (arg && expr.delegate) { - let result = self.makeTempVar(); - - self.emit(t.returnStatement(t.callExpression( - self.contextProperty("delegateYield"), [ - arg, - t.stringLiteral(result.property.name), - after - ] - ))); - - self.mark(after); - - return result; - } - - self.emitAssign(self.contextProperty("next"), after); - self.emit(t.returnStatement(arg || null)); - self.mark(after); - - return self.contextProperty("sent"); - - default: - throw new Error( - "unknown Expression of type " + - JSON.stringify(expr.type)); - } -}; diff --git a/packages/babel-plugin-transform-regenerator/src/hoist.js b/packages/babel-plugin-transform-regenerator/src/hoist.js deleted file mode 100644 index cac505fdfe127..0000000000000 --- a/packages/babel-plugin-transform-regenerator/src/hoist.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import * as t from "babel-types"; -let hasOwn = Object.prototype.hasOwnProperty; - -// The hoist function takes a FunctionExpression or FunctionDeclaration -// and replaces any Declaration nodes in its body with assignments, then -// returns a VariableDeclaration containing just the names of the removed -// declarations. -exports.hoist = function(funPath) { - t.assertFunction(funPath.node); - - let vars = {}; - - function varDeclToExpr(vdec, includeIdentifiers) { - t.assertVariableDeclaration(vdec); - // TODO assert.equal(vdec.kind, "var"); - let exprs = []; - - vdec.declarations.forEach(function(dec) { - // Note: We duplicate 'dec.id' here to ensure that the variable declaration IDs don't - // have the same 'loc' value, since that can make sourcemaps and retainLines behave poorly. - vars[dec.id.name] = t.identifier(dec.id.name); - - if (dec.init) { - exprs.push(t.assignmentExpression( - "=", dec.id, dec.init - )); - } else if (includeIdentifiers) { - exprs.push(dec.id); - } - }); - - if (exprs.length === 0) - return null; - - if (exprs.length === 1) - return exprs[0]; - - return t.sequenceExpression(exprs); - } - - funPath.get("body").traverse({ - VariableDeclaration: { - exit: function(path) { - let expr = varDeclToExpr(path.node, false); - if (expr === null) { - path.remove(); - } else { - // We don't need to traverse this expression any further because - // there can't be any new declarations inside an expression. - path.replaceWith(t.expressionStatement(expr)); - } - - // Since the original node has been either removed or replaced, - // avoid traversing it any further. - path.skip(); - } - }, - - ForStatement: function(path) { - let init = path.node.init; - if (t.isVariableDeclaration(init)) { - path.get("init").replaceWith(varDeclToExpr(init, false)); - } - }, - - ForXStatement: function(path) { - let left = path.get("left"); - if (left.isVariableDeclaration()) { - left.replaceWith(varDeclToExpr(left.node, true)); - } - }, - - FunctionDeclaration: function(path) { - let node = path.node; - vars[node.id.name] = node.id; - - let assignment = t.expressionStatement( - t.assignmentExpression( - "=", - node.id, - t.functionExpression( - node.id, - node.params, - node.body, - node.generator, - node.expression - ) - ) - ); - - if (path.parentPath.isBlockStatement()) { - // Insert the assignment form before the first statement in the - // enclosing block. - path.parentPath.unshiftContainer("body", assignment); - - // Remove the function declaration now that we've inserted the - // equivalent assignment form at the beginning of the block. - path.remove(); - } else { - // If the parent node is not a block statement, then we can just - // replace the declaration with the equivalent assignment form - // without worrying about hoisting it. - path.replaceWith(assignment); - } - - // Don't hoist variables out of inner functions. - path.skip(); - }, - - FunctionExpression: function(path) { - // Don't descend into nested function expressions. - path.skip(); - } - }); - - let paramNames = {}; - funPath.get("params").forEach(function(paramPath) { - let param = paramPath.node; - if (t.isIdentifier(param)) { - paramNames[param.name] = param; - } else { - // Variables declared by destructuring parameter patterns will be - // harmlessly re-declared. - } - }); - - let declarations = []; - - Object.keys(vars).forEach(function(name) { - if (!hasOwn.call(paramNames, name)) { - declarations.push(t.variableDeclarator(vars[name], null)); - } - }); - - if (declarations.length === 0) { - return null; // Be sure to handle this case! - } - - return t.variableDeclaration("var", declarations); -}; diff --git a/packages/babel-plugin-transform-regenerator/src/index.js b/packages/babel-plugin-transform-regenerator/src/index.js index 34fea7a15a985..ba3acbf5b92f6 100644 --- a/packages/babel-plugin-transform-regenerator/src/index.js +++ b/packages/babel-plugin-transform-regenerator/src/index.js @@ -8,6 +8,4 @@ * the same directory. */ -export default function () { - return require("./visit"); -} +export default require("regenerator-transform"); diff --git a/packages/babel-plugin-transform-regenerator/src/leap.js b/packages/babel-plugin-transform-regenerator/src/leap.js deleted file mode 100644 index 047a2bf263a43..0000000000000 --- a/packages/babel-plugin-transform-regenerator/src/leap.js +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import assert from "assert"; -import * as t from "babel-types"; -import { inherits } from "util"; - -function Entry() { - assert.ok(this instanceof Entry); -} - -function FunctionEntry(returnLoc) { - Entry.call(this); - t.assertLiteral(returnLoc); - this.returnLoc = returnLoc; -} - -inherits(FunctionEntry, Entry); -exports.FunctionEntry = FunctionEntry; - -function LoopEntry(breakLoc, continueLoc, label) { - Entry.call(this); - - t.assertLiteral(breakLoc); - t.assertLiteral(continueLoc); - - if (label) { - t.assertIdentifier(label); - } else { - label = null; - } - - this.breakLoc = breakLoc; - this.continueLoc = continueLoc; - this.label = label; -} - -inherits(LoopEntry, Entry); -exports.LoopEntry = LoopEntry; - -function SwitchEntry(breakLoc) { - Entry.call(this); - t.assertLiteral(breakLoc); - this.breakLoc = breakLoc; -} - -inherits(SwitchEntry, Entry); -exports.SwitchEntry = SwitchEntry; - -function TryEntry(firstLoc, catchEntry, finallyEntry) { - Entry.call(this); - - t.assertLiteral(firstLoc); - - if (catchEntry) { - assert.ok(catchEntry instanceof CatchEntry); - } else { - catchEntry = null; - } - - if (finallyEntry) { - assert.ok(finallyEntry instanceof FinallyEntry); - } else { - finallyEntry = null; - } - - // Have to have one or the other (or both). - assert.ok(catchEntry || finallyEntry); - - this.firstLoc = firstLoc; - this.catchEntry = catchEntry; - this.finallyEntry = finallyEntry; -} - -inherits(TryEntry, Entry); -exports.TryEntry = TryEntry; - -function CatchEntry(firstLoc, paramId) { - Entry.call(this); - - t.assertLiteral(firstLoc); - t.assertIdentifier(paramId); - - this.firstLoc = firstLoc; - this.paramId = paramId; -} - -inherits(CatchEntry, Entry); -exports.CatchEntry = CatchEntry; - -function FinallyEntry(firstLoc, afterLoc) { - Entry.call(this); - t.assertLiteral(firstLoc); - t.assertLiteral(afterLoc); - this.firstLoc = firstLoc; - this.afterLoc = afterLoc; -} - -inherits(FinallyEntry, Entry); -exports.FinallyEntry = FinallyEntry; - -function LabeledEntry(breakLoc, label) { - Entry.call(this); - - t.assertLiteral(breakLoc); - t.assertIdentifier(label); - - this.breakLoc = breakLoc; - this.label = label; -} - -inherits(LabeledEntry, Entry); -exports.LabeledEntry = LabeledEntry; - -function LeapManager(emitter) { - assert.ok(this instanceof LeapManager); - - let Emitter = require("./emit").Emitter; - assert.ok(emitter instanceof Emitter); - - this.emitter = emitter; - this.entryStack = [new FunctionEntry(emitter.finalLoc)]; -} - -let LMp = LeapManager.prototype; -exports.LeapManager = LeapManager; - -LMp.withEntry = function(entry, callback) { - assert.ok(entry instanceof Entry); - this.entryStack.push(entry); - try { - callback.call(this.emitter); - } finally { - let popped = this.entryStack.pop(); - assert.strictEqual(popped, entry); - } -}; - -LMp._findLeapLocation = function(property, label) { - for (let i = this.entryStack.length - 1; i >= 0; --i) { - let entry = this.entryStack[i]; - let loc = entry[property]; - if (loc) { - if (label) { - if (entry.label && - entry.label.name === label.name) { - return loc; - } - } else if (entry instanceof LabeledEntry) { - // Ignore LabeledEntry entries unless we are actually breaking to - // a label. - } else { - return loc; - } - } - } - - return null; -}; - -LMp.getBreakLoc = function(label) { - return this._findLeapLocation("breakLoc", label); -}; - -LMp.getContinueLoc = function(label) { - return this._findLeapLocation("continueLoc", label); -}; diff --git a/packages/babel-plugin-transform-regenerator/src/meta.js b/packages/babel-plugin-transform-regenerator/src/meta.js deleted file mode 100644 index dc7ffcdd45a1f..0000000000000 --- a/packages/babel-plugin-transform-regenerator/src/meta.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import assert from "assert"; -let m = require("private").makeAccessor(); -import * as t from "babel-types"; -let hasOwn = Object.prototype.hasOwnProperty; - -function makePredicate(propertyName, knownTypes) { - function onlyChildren(node) { - t.assertNode(node); - - // Assume no side effects until we find out otherwise. - let result = false; - - function check(child) { - if (result) { - // Do nothing. - } else if (Array.isArray(child)) { - child.some(check); - } else if (t.isNode(child)) { - assert.strictEqual(result, false); - result = predicate(child); - } - return result; - } - - let keys = t.VISITOR_KEYS[node.type]; - if (keys) { - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let child = node[key]; - check(child); - } - } - - return result; - } - - function predicate(node) { - t.assertNode(node); - - let meta = m(node); - if (hasOwn.call(meta, propertyName)) - return meta[propertyName]; - - // Certain types are "opaque," which means they have no side - // effects or leaps and we don't care about their subexpressions. - if (hasOwn.call(opaqueTypes, node.type)) - return meta[propertyName] = false; - - if (hasOwn.call(knownTypes, node.type)) - return meta[propertyName] = true; - - return meta[propertyName] = onlyChildren(node); - } - - predicate.onlyChildren = onlyChildren; - - return predicate; -} - -let opaqueTypes = { - FunctionExpression: true -}; - -// These types potentially have side effects regardless of what side -// effects their subexpressions have. -let sideEffectTypes = { - CallExpression: true, // Anything could happen! - ForInStatement: true, // Modifies the key variable. - UnaryExpression: true, // Think delete. - BinaryExpression: true, // Might invoke .toString() or .valueOf(). - AssignmentExpression: true, // Side-effecting by definition. - UpdateExpression: true, // Updates are essentially assignments. - NewExpression: true // Similar to CallExpression. -}; - -// These types are the direct cause of all leaps in control flow. -let leapTypes = { - YieldExpression: true, - BreakStatement: true, - ContinueStatement: true, - ReturnStatement: true, - ThrowStatement: true -}; - -// All leap types are also side effect types. -for (let type in leapTypes) { - if (hasOwn.call(leapTypes, type)) { - sideEffectTypes[type] = leapTypes[type]; - } -} - -exports.hasSideEffects = makePredicate("hasSideEffects", sideEffectTypes); -exports.containsLeap = makePredicate("containsLeap", leapTypes); diff --git a/packages/babel-plugin-transform-regenerator/src/util.js b/packages/babel-plugin-transform-regenerator/src/util.js deleted file mode 100644 index 78bfa0f28b107..0000000000000 --- a/packages/babel-plugin-transform-regenerator/src/util.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import * as t from "babel-types"; - -export function runtimeProperty(name) { - return t.memberExpression( - t.identifier("regeneratorRuntime"), - t.identifier(name), - false - ); -} - -export function isReference(path) { - return path.isReferenced() || path.parentPath.isAssignmentExpression({ left: path.node }); -} diff --git a/packages/babel-plugin-transform-regenerator/src/visit.js b/packages/babel-plugin-transform-regenerator/src/visit.js deleted file mode 100644 index 8d96f2b40495c..0000000000000 --- a/packages/babel-plugin-transform-regenerator/src/visit.js +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import assert from "assert"; -import * as t from "babel-types"; -import { hoist } from "./hoist"; -import { Emitter } from "./emit"; -import * as util from "./util"; - -let getMarkInfo = require("private").makeAccessor(); - -exports.visitor = { - Function: { - exit: function(path, state) { - let node = path.node; - - if (node.generator) { - if (node.async) { - // Async generator - if (state.opts.asyncGenerators === false) return; - } else { - // Plain generator - if (state.opts.generators === false) return; - } - } else if (node.async) { - // Async function - if (state.opts.async === false) return; - } else { - // Not a generator or async function. - return; - } - - let contextId = path.scope.generateUidIdentifier("context"); - let argsId = path.scope.generateUidIdentifier("args"); - - path.ensureBlock(); - let bodyBlockPath = path.get("body"); - - if (node.async) { - bodyBlockPath.traverse(awaitVisitor); - } - - bodyBlockPath.traverse(functionSentVisitor, { - context: contextId - }); - - let outerBody = []; - let innerBody = []; - - bodyBlockPath.get("body").forEach(function(childPath) { - let node = childPath.node; - if (node && node._blockHoist != null) { - outerBody.push(node); - } else { - innerBody.push(node); - } - }); - - if (outerBody.length > 0) { - // Only replace the inner body if we actually hoisted any statements - // to the outer body. - bodyBlockPath.node.body = innerBody; - } - - let outerFnExpr = getOuterFnExpr(path); - // Note that getOuterFnExpr has the side-effect of ensuring that the - // function has a name (so node.id will always be an Identifier), even - // if a temporary name has to be synthesized. - t.assertIdentifier(node.id); - let innerFnId = t.identifier(node.id.name + "$"); - - // Turn all declarations into vars, and replace the original - // declarations with equivalent assignment expressions. - let vars = hoist(path); - - let didRenameArguments = renameArguments(path, argsId); - if (didRenameArguments) { - vars = vars || t.variableDeclaration("var", []); - vars.declarations.push(t.variableDeclarator( - argsId, t.identifier("arguments") - )); - } - - let emitter = new Emitter(contextId); - emitter.explode(path.get("body")); - - if (vars && vars.declarations.length > 0) { - outerBody.push(vars); - } - - let wrapArgs = [ - emitter.getContextFunction(innerFnId), - // Async functions that are not generators don't care about the - // outer function because they don't need it to be marked and don't - // inherit from its .prototype. - node.generator ? outerFnExpr : t.nullLiteral(), - t.thisExpression() - ]; - - let tryLocsList = emitter.getTryLocsList(); - if (tryLocsList) { - wrapArgs.push(tryLocsList); - } - - let wrapCall = t.callExpression( - util.runtimeProperty(node.async ? "async" : "wrap"), - wrapArgs - ); - - outerBody.push(t.returnStatement(wrapCall)); - node.body = t.blockStatement(outerBody); - - let wasGeneratorFunction = node.generator; - if (wasGeneratorFunction) { - node.generator = false; - } - - if (node.async) { - node.async = false; - } - - if (wasGeneratorFunction && t.isExpression(node)) { - path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node])); - } - - // Generators are processed in 'exit' handlers so that regenerator only has to run on - // an ES5 AST, but that means traversal will not pick up newly inserted references - // to things like 'regeneratorRuntime'. To avoid this, we explicitly requeue. - path.requeue(); - } - } -}; - -// Given a NodePath for a Function, return an Expression node that can be -// used to refer reliably to the function object from inside the function. -// This expression is essentially a replacement for arguments.callee, with -// the key advantage that it works in strict mode. -function getOuterFnExpr(funPath) { - let node = funPath.node; - t.assertFunction(node); - - if (!node.id) { - // Default-exported function declarations, and function expressions may not - // have a name to reference, so we explicitly add one. - node.id = funPath.scope.parent.generateUidIdentifier("callee"); - } - - if (node.generator && // Non-generator functions don't need to be marked. - t.isFunctionDeclaration(node)) { - let pp = funPath.findParent(function (path) { - return path.isProgram() || path.isBlockStatement(); - }); - - if (!pp) { - return node.id; - } - - let markDecl = getRuntimeMarkDecl(pp); - let markedArray = markDecl.declarations[0].id; - let funDeclIdArray = markDecl.declarations[0].init.callee.object; - t.assertArrayExpression(funDeclIdArray); - - let index = funDeclIdArray.elements.length; - funDeclIdArray.elements.push(node.id); - - return t.memberExpression( - markedArray, - t.numericLiteral(index), - true - ); - } - - return node.id; -} - -function getRuntimeMarkDecl(blockPath) { - let block = blockPath.node; - assert.ok(Array.isArray(block.body)); - - let info = getMarkInfo(block); - if (info.decl) { - return info.decl; - } - - info.decl = t.variableDeclaration("var", [ - t.variableDeclarator( - blockPath.scope.generateUidIdentifier("marked"), - t.callExpression( - t.memberExpression( - t.arrayExpression([]), - t.identifier("map"), - false - ), - [util.runtimeProperty("mark")] - ) - ) - ]); - - blockPath.unshiftContainer("body", info.decl); - - return info.decl; -} - -function renameArguments(funcPath, argsId) { - let state = { - didRenameArguments: false, - argsId: argsId - }; - - funcPath.traverse(argumentsVisitor, state); - - // If the traversal replaced any arguments references, then we need to - // alias the outer function's arguments binding (be it the implicit - // arguments object or some other parameter or variable) to the variable - // named by argsId. - return state.didRenameArguments; -} - -let argumentsVisitor = { - "FunctionExpression|FunctionDeclaration": function(path) { - path.skip(); - }, - - Identifier: function(path, state) { - if (path.node.name === "arguments" && util.isReference(path)) { - path.replaceWith(state.argsId); - state.didRenameArguments = true; - } - } -}; - -let functionSentVisitor = { - MetaProperty(path) { - let { node } = path; - - if (node.meta.name === "function" && node.property.name === "sent") { - path.replaceWith(t.memberExpression(this.context, t.identifier("_sent"))); - } - } -}; - -let awaitVisitor = { - Function: function(path) { - path.skip(); // Don't descend into nested function scopes. - }, - - AwaitExpression: function(path) { - // Convert await expressions to yield expressions. - let argument = path.node.argument; - - // Transforming `await x` to `yield regeneratorRuntime.awrap(x)` - // causes the argument to be wrapped in such a way that the runtime - // can distinguish between awaited and merely yielded values. - path.replaceWith(t.yieldExpression( - t.callExpression( - util.runtimeProperty("awrap"), - [argument] - ), - false - )); - } -};