Skip to content

Instantly share code, notes, and snippets.

@koush
Created July 5, 2019 21:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save koush/3683fc2ea10c6b5a31ef203379f6a47c to your computer and use it in GitHub Desktop.
Save koush/3683fc2ea10c6b5a31ef203379f6a47c to your computer and use it in GitHub Desktop.
let currentTypes = null;
function wrapWithTypes (types, fn) {
return function (...args) {
const oldTypes = currentTypes;
currentTypes = types;
try {
return fn.apply(this, args);
} finally {
currentTypes = oldTypes;
}
};
}
function wrapDuktapeThread (argument) {
return [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "__thread"
},
"init": {
"type": "NewExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "Duktape"
},
"property": {
"type": "Identifier",
"name": "Thread"
},
"computed": false
},
"arguments": [
argument
]
}
}
],
"kind": "var"
},
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "__iterDone"
},
"init": null
}
],
"kind": "var"
},
{
"type": "ReturnStatement",
"argument": {
"type": "ObjectExpression",
"properties": [
{
"type": "ObjectMethod",
"method": true,
"shorthand": false,
"computed": false,
"key": {
"type": "Identifier",
"name": "next"
},
"kind": "method",
"id": null,
"generator": false,
"expression": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"body": [
{
"type": "IfStatement",
"test": {
"type": "UnaryExpression",
"operator": "!",
"prefix": true,
"argument": {
"type": "Identifier",
"name": "__iterDone"
},
"extra": {
"parenthesizedArgument": false
}
},
"consequent": {
"type": "BlockStatement",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "ret"
},
"init": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "Duktape"
},
"property": {
"type": "Identifier",
"name": "Thread"
},
"computed": false
},
"property": {
"type": "Identifier",
"name": "resume"
},
"computed": false
},
"arguments": [
{
"type": "Identifier",
"name": "__thread"
}
]
}
}
],
"kind": "var"
},
{
"type": "IfStatement",
"test": {
"type": "LogicalExpression",
"left": {
"type": "Identifier",
"name": "ret"
},
"operator": "&&",
"right": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "ret"
},
"property": {
"type": "Identifier",
"name": "__iter"
},
"computed": false
}
},
"consequent": {
"type": "BlockStatement",
"body": [
{
"type": "ReturnStatement",
"argument": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "ret"
},
"property": {
"type": "Identifier",
"name": "next"
},
"computed": false
}
}
],
"directives": []
},
"alternate": null
}
],
"directives": []
},
"alternate": null
},
{
"type": "ReturnStatement",
"argument": {
"type": "AssignmentExpression",
"operator": "=",
"left": {
"type": "Identifier",
"name": "__iterDone"
},
"right": {
"type": "ObjectExpression",
"properties": [
{
"type": "ObjectProperty",
"method": false,
"shorthand": false,
"computed": false,
"key": {
"type": "Identifier",
"name": "value"
},
"value": {
"type": "Identifier",
"name": "ret"
}
},
{
"type": "ObjectProperty",
"method": false,
"shorthand": false,
"computed": false,
"key": {
"type": "Identifier",
"name": "done"
},
"value": {
"type": "BooleanLiteral",
"value": true
}
}
]
}
}
}
],
"directives": []
}
}
]
}
}
];
}
function wrapYieldStatement (argument) {
return {
"type": "ExpressionStatement",
"expression": {
"type": "CallExpression",
"callee": {
"type": "MemberExpression",
"object": {
"type": "MemberExpression",
"object": {
"type": "Identifier",
"name": "Duktape"
},
"property": {
"type": "Identifier",
"name": "Thread"
},
"computed": false
},
"property": {
"type": "Identifier",
"name": "yield"
},
"computed": false
},
"arguments": [
{
"type": "ObjectExpression",
"properties": [
{
"type": "ObjectProperty",
"method": false,
"shorthand": false,
"computed": false,
"key": {
"type": "Identifier",
"name": "__iter"
},
"value": {
"type": "BooleanLiteral",
"value": true
}
},
{
"type": "ObjectProperty",
"method": false,
"shorthand": false,
"computed": false,
"key": {
"type": "Identifier",
"name": "next"
},
"value": {
"type": "ObjectExpression",
"properties": [
{
"type": "ObjectProperty",
"method": false,
"shorthand": false,
"computed": false,
"key": {
"type": "Identifier",
"name": "done"
},
"value": {
"type": "BooleanLiteral",
"value": false
}
},
{
"type": "ObjectProperty",
"method": false,
"shorthand": false,
"computed": false,
"key": {
"type": "Identifier",
"name": "value"
},
"value": argument
}
]
}
}
]
}
]
}
}
}
module.exports = ({ types: t }) => {
return {
visitor: {
YieldExpression (path, state) {
console.log();
const node = t.cloneNode(path.get("argument").node);
// path.get(body).set("body", )
path.replaceWith(wrapYieldStatement(node));
},
Method (path, state) {
let node = path.node;
if (!shouldRegenerate(node, state)) return;
const container = t.functionExpression(
null,
[],
t.cloneNode(node.body, false),
node.generator,
node.async,
);
path.get("body").set("body", [
t.returnStatement(
t.callExpression(container, []),
),
]);
// Regardless of whether or not the wrapped function is a an async method
// or generator the outer function should not be
node.generator = false;
// Unwrap the wrapper IIFE's environment so super and this and such still work.
path
.get("body.body.0.argument.callee")
.unwrapFunctionEnvironment();
},
Function: {
exit: wrapWithTypes(t, function (path, state) {
console.log();
let node = path.node;
if (!shouldRegenerate(node, state)) return;
const container = t.functionExpression(
null,
[],
t.cloneNode(node.body, false),
false,
node.async,
);
node.generator = false;
path.get("body").set("body",
wrapDuktapeThread(container)
);
path
.get("body.body.0.declarations.0.init.arguments.0")
.unwrapFunctionEnvironment();
})
}
}
}
}
// Check if a node should be transformed by regenerator
function shouldRegenerate (node, state) {
return node.generator
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment