New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Zenparsing async generator functions #4576
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import * as t from "babel-types"; | ||
import template from "babel-template"; | ||
import traverse from "babel-traverse"; | ||
|
||
let buildForAwait = template(` | ||
function* wrapper() { | ||
var ITERATOR_COMPLETION = true; | ||
var ITERATOR_HAD_ERROR_KEY = false; | ||
var ITERATOR_ERROR_KEY = undefined; | ||
try { | ||
for ( | ||
var ITERATOR_KEY = GET_ITERATOR(OBJECT), STEP_KEY, STEP_VALUE; | ||
( | ||
STEP_KEY = yield AWAIT(ITERATOR_KEY.next()), | ||
ITERATOR_COMPLETION = STEP_KEY.done, | ||
STEP_VALUE = yield AWAIT(STEP_KEY.value), | ||
!ITERATOR_COMPLETION | ||
); | ||
ITERATOR_COMPLETION = true) { | ||
} | ||
} catch (err) { | ||
ITERATOR_HAD_ERROR_KEY = true; | ||
ITERATOR_ERROR_KEY = err; | ||
} finally { | ||
try { | ||
if (!ITERATOR_COMPLETION && ITERATOR_KEY.return) { | ||
yield AWAIT(ITERATOR_KEY.return()); | ||
} | ||
} finally { | ||
if (ITERATOR_HAD_ERROR_KEY) { | ||
throw ITERATOR_ERROR_KEY; | ||
} | ||
} | ||
} | ||
} | ||
`); | ||
|
||
let forAwaitVisitor = { | ||
noScope: true, | ||
|
||
Identifier(path, replacements) { | ||
if (path.node.name in replacements) { | ||
path.replaceInline(replacements[path.node.name]); | ||
} | ||
}, | ||
|
||
CallExpression(path, replacements) { | ||
let callee = path.node.callee; | ||
|
||
// if no await wrapping is being applied, unwrap the call expression | ||
if (t.isIdentifier(callee) && callee.name === "AWAIT" && !replacements.AWAIT) { | ||
path.replaceWith(path.node.arguments[0]); | ||
} | ||
} | ||
}; | ||
|
||
export default function (path, helpers) { | ||
let { node, scope, parent } = path; | ||
|
||
let stepKey = scope.generateUidIdentifier("step"); | ||
let stepValue = scope.generateUidIdentifier("value"); | ||
let left = node.left; | ||
let declar; | ||
|
||
if (t.isIdentifier(left) || t.isPattern(left) || t.isMemberExpression(left)) { | ||
// for await (i of test), for await ({ i } of test) | ||
declar = t.expressionStatement(t.assignmentExpression("=", left, stepValue)); | ||
} else if (t.isVariableDeclaration(left)) { | ||
// for await (let i of test) | ||
declar = t.variableDeclaration(left.kind, [ | ||
t.variableDeclarator(left.declarations[0].id, stepValue) | ||
]); | ||
} | ||
|
||
let template = buildForAwait(); | ||
|
||
traverse(template, forAwaitVisitor, null, { | ||
ITERATOR_HAD_ERROR_KEY: scope.generateUidIdentifier("didIteratorError"), | ||
ITERATOR_COMPLETION: scope.generateUidIdentifier("iteratorNormalCompletion"), | ||
ITERATOR_ERROR_KEY: scope.generateUidIdentifier("iteratorError"), | ||
ITERATOR_KEY: scope.generateUidIdentifier("iterator"), | ||
GET_ITERATOR: helpers.getAsyncIterator, | ||
OBJECT: node.right, | ||
STEP_VALUE: stepValue, | ||
STEP_KEY: stepKey, | ||
AWAIT: helpers.wrapAwait | ||
}); | ||
|
||
// remove generator function wrapper | ||
template = template.body.body; | ||
|
||
let isLabeledParent = t.isLabeledStatement(parent); | ||
let tryBody = template[3].block.body; | ||
let loop = tryBody[0]; | ||
|
||
if (isLabeledParent) { | ||
tryBody[0] = t.labeledStatement(parent.label, loop); | ||
} | ||
|
||
return { | ||
replaceParent: isLabeledParent, | ||
node: template, | ||
declar, | ||
loop | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,14 +47,15 @@ function run(task) { | |
|
||
let execCode = exec.code; | ||
let result; | ||
let resultExec; | ||
|
||
if (execCode) { | ||
let execOpts = getOpts(exec); | ||
result = babel.transform(execCode, execOpts); | ||
execCode = result.code; | ||
|
||
try { | ||
runExec(execOpts, execCode); | ||
resultExec = runExec(execOpts, execCode); | ||
} catch (err) { | ||
err.message = exec.loc + ": " + err.message; | ||
err.message += codeFrame(execCode); | ||
|
@@ -90,6 +91,10 @@ function run(task) { | |
chai.expect({ line: expect.line, column: expect.column }).to.deep.equal(actual); | ||
}); | ||
} | ||
|
||
if (execCode && resultExec) { | ||
return resultExec; | ||
} | ||
} | ||
|
||
function runExec(opts, execCode) { | ||
|
@@ -151,7 +156,14 @@ export default function ( | |
return throwMsg === true || err.message.indexOf(throwMsg) >= 0; | ||
}); | ||
} else { | ||
runTask(); | ||
if (task.exec.code) { | ||
let result = run(task); | ||
if (result && typeof result.then === "function") { | ||
return result; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically just returning the result of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the |
||
} | ||
} else { | ||
runTask(); | ||
} | ||
} | ||
}); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we try to handle the promise case here too, to allow async tests to reject.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should, do you want to do that in another pr if you want to and with an example test?