Skip to content

Commit

Permalink
Merge AsyncGenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
Sukka committed May 24, 2024
1 parent cc7063b commit 257f23a
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 186 deletions.
46 changes: 16 additions & 30 deletions packages/babel-helpers/src/helpers-generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,6 @@ function helper(
export { helpers as default };
const helpers: Record<string, Helper> = {
__proto__: null,
// size: 1095, gzip size: 500
AsyncGenerator: helper(
"7.0.0-beta.0",
'function AsyncGeneratorImpl(e){var r,t;function resume(r,t){try{var n=e[r](t),o=n.value,u=o instanceof OverloadYield;Promise.resolve(u?o.v:o).then((function(t){if(u){var i="return"===r?"return":"next";if(!o.k||t.done)return void resume(i,t);t=e[i](t).value}settle(n.done?"return":"normal",t)}),(function(e){resume("throw",e)}))}catch(e){settle("throw",e)}}function settle(e,n){switch(e){case"return":r.resolve({value:n,done:!0});break;case"throw":r.reject(n);break;default:r.resolve({value:n,done:!1})}(r=r.next)?resume(r.key,r.arg):t=null}this._invoke=function(e,n){return new Promise((function(o,u){var i={key:e,arg:n,resolve:o,reject:u,next:null};t?t=t.next=i:(r=t=i,resume(e,n))}))},"function"!=typeof e.return&&(this.return=void 0)}AsyncGeneratorImpl.prototype["function"==typeof Symbol&&Symbol.asyncIterator||"@@asyncIterator"]=function(){return this},AsyncGeneratorImpl.prototype.next=function(e){return this._invoke("next",e)},AsyncGeneratorImpl.prototype.throw=function(e){return this._invoke("throw",e)},AsyncGeneratorImpl.prototype.return=function(e){return this._invoke("return",e)};',
{
globals: ["Promise", "Symbol"],
locals: {
AsyncGeneratorImpl: [
"body.0.id",
"body.1.expression.expressions.0.left.object.object",
"body.1.expression.expressions.1.left.object.object",
"body.1.expression.expressions.2.left.object.object",
"body.1.expression.expressions.3.left.object.object",
],
},
exportBindingAssignments: [],
exportName: "AsyncGeneratorImpl",
dependencies: {
OverloadYield: [
"body.0.body.body.1.body.body.0.block.body.0.declarations.2.init.right",
],
},
},
),
// size: 47, gzip size: 63
OverloadYield: helper(
"7.18.14",
Expand Down Expand Up @@ -1382,18 +1358,28 @@ const helpers: Record<string, Helper> = {
dependencies: {},
},
),
// size: 102, gzip size: 96
// size: 1177, gzip size: 528
wrapAsyncGenerator: helper(
"7.0.0-beta.0",
"function _wrapAsyncGenerator(r){return function(){return new AsyncGenerator(r.apply(this,arguments))}}",
'function _wrapAsyncGenerator(e){return function(){return new AsyncGenerator(e.apply(this,arguments))}}function AsyncGenerator(e){var r,t;function resume(r,t){try{var n=e[r](t),o=n.value,u=o instanceof OverloadYield;Promise.resolve(u?o.v:o).then((function(t){if(u){var i="return"===r?"return":"next";if(!o.k||t.done)return void resume(i,t);t=e[i](t).value}settle(n.done?"return":"normal",t)}),(function(e){resume("throw",e)}))}catch(e){settle("throw",e)}}function settle(e,n){switch(e){case"return":r.resolve({value:n,done:!0});break;case"throw":r.reject(n);break;default:r.resolve({value:n,done:!1})}(r=r.next)?resume(r.key,r.arg):t=null}this._invoke=function(e,n){return new Promise((function(o,u){var i={key:e,arg:n,resolve:o,reject:u,next:null};t?t=t.next=i:(r=t=i,resume(e,n))}))},"function"!=typeof e.return&&(this.return=void 0)}AsyncGenerator.prototype["function"==typeof Symbol&&Symbol.asyncIterator||"@@asyncIterator"]=function(){return this},AsyncGenerator.prototype.next=function(e){return this._invoke("next",e)},AsyncGenerator.prototype.throw=function(e){return this._invoke("throw",e)},AsyncGenerator.prototype.return=function(e){return this._invoke("return",e)};',
{
globals: [],
locals: { _wrapAsyncGenerator: ["body.0.id"] },
globals: ["Promise", "Symbol"],
locals: {
_wrapAsyncGenerator: ["body.0.id"],
AsyncGenerator: [
"body.1.id",
"body.0.body.body.0.argument.body.body.0.argument.callee",
"body.2.expression.expressions.0.left.object.object",
"body.2.expression.expressions.1.left.object.object",
"body.2.expression.expressions.2.left.object.object",
"body.2.expression.expressions.3.left.object.object",
],
},
exportBindingAssignments: [],
exportName: "_wrapAsyncGenerator",
dependencies: {
AsyncGenerator: [
"body.0.body.body.0.argument.body.body.0.argument.callee",
OverloadYield: [
"body.1.body.body.1.body.body.0.block.body.0.declarations.2.init.right",
],
},
},
Expand Down
155 changes: 0 additions & 155 deletions packages/babel-helpers/src/helpers/AsyncGenerator.ts

This file was deleted.

154 changes: 153 additions & 1 deletion packages/babel-helpers/src/helpers/wrapAsyncGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,162 @@
/* @minVersion 7.0.0-beta.0 */

import AsyncGenerator from "./AsyncGenerator.ts";
import OverloadYield from "./OverloadYield.ts";

export default function _wrapAsyncGenerator(fn: GeneratorFunction) {
return function (this: any) {
// Use "arguments" here for better compatibility and smaller bundle size
return new AsyncGenerator(fn.apply(this, arguments as any));
};
}

/* == The implementation of the AsyncGenerator class == */

type AsyncIteratorMethod = "next" | "throw" | "return";

declare class AsyncGenerator<T = unknown, TReturn = any, TNext = unknown>
implements globalThis.AsyncGenerator<T, TReturn, TNext>
{
_invoke: (
key: AsyncIteratorMethod,
arg: IteratorResult<T>,
) => Promise<IteratorResult<T, TReturn>>;

constructor(gen: Generator<T, TReturn, TNext>);

next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
return(
value: TReturn | PromiseLike<TReturn>,
): Promise<IteratorResult<T, TReturn>>;
throw(e: any): Promise<IteratorResult<T, TReturn>>;
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
}

interface AsyncGeneratorRequest<T = unknown, TReturn = any, TNext = unknown> {
key: AsyncIteratorMethod;
arg: IteratorResult<T>;
resolve: (value: IteratorResult<T, TReturn>) => void;
reject: (error: any) => void;
next: AsyncGeneratorRequest<T, TReturn, TNext> | null;
}

function AsyncGenerator<T = unknown, TReturn = any, TNext = unknown>(
this: AsyncGenerator<T, TReturn, TNext>,
gen: Generator<T, TReturn, TNext>,
) {
var front: AsyncGeneratorRequest<T, TReturn, TNext> | null,
back: AsyncGeneratorRequest<T, TReturn, TNext> | null;

function send(key: AsyncIteratorMethod, arg: IteratorResult<T>) {
return new Promise<IteratorResult<T, TReturn>>(function (resolve, reject) {
var request: AsyncGeneratorRequest<T, TReturn, TNext> = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null,
};

if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}

function resume(key: AsyncIteratorMethod, arg: IteratorResult<T, TReturn>) {
try {
var result = gen[key](arg);
var value = result.value;
var overloaded = value instanceof OverloadYield;

Promise.resolve(
overloaded ? (value as OverloadYield<T | TReturn>).v : value,
).then(
function (arg: any) {
if (overloaded) {
// Overloaded yield requires calling into the generator twice:
// - first we get the iterator result wrapped in a promise
// (the gen[key](arg) call above)
// - then we await it (the Promise.resolve call above)
// - then we give the result back to the iterator, so that it can:
// * if it was an await, use its result
// * if it was a yield*, possibly return the `done: true` signal
// so that yield* knows that the iterator is finished.
// This needs to happen in the second call, because in the
// first one `done: true` was hidden in the promise and thus
// not visible to the (sync) yield*.
// The other part of this implementation is in asyncGeneratorDelegate.
var nextKey: "return" | "next" =
key === "return" ? "return" : "next";
if (
!(value as OverloadYield<IteratorReturnResult<T>>).k ||
arg.done
) {
// await or end of yield*
resume(nextKey, arg);
return;
} else {
// yield*, not done
arg = gen[nextKey](arg).value;
}
}

settle(result.done ? "return" : "normal", arg);
},
function (err) {
resume("throw", err);
},
);
} catch (err) {
settle("throw", err);
}
}

function settle(type: AsyncIteratorMethod | "normal", value: any) {
switch (type) {
case "return":
front!.resolve({ value: value, done: true });
break;
case "throw":
front!.reject(value);
break;
default:
front!.resolve({ value: value, done: false });
break;
}

front = front!.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}

this._invoke = send;

// Hide "return" method if generator return is not supported
if (typeof gen.return !== "function") {
// @ts-expect-error -- intentionally remove "return" when not supported
this.return = undefined;
}
}

AsyncGenerator.prototype[
((typeof Symbol === "function" && Symbol.asyncIterator) ||
"@@asyncIterator") as typeof Symbol.asyncIterator
] = function () {
return this;
};

AsyncGenerator.prototype.next = function (arg: IteratorResult<any>) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg: IteratorResult<any>) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg: IteratorResult<any>) {
return this._invoke("return", arg);
};

0 comments on commit 257f23a

Please sign in to comment.