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
Restore traversal context after enter / traverse #13813
Changes from 3 commits
0ed3458
a77931f
e8a3e82
ea9821b
024b7aa
ef44524
f11e12d
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,11 @@ | ||
async function main() { | ||
async () => { | ||
// IIFE: required for babel to crash | ||
for await (const string of async_iterable) { | ||
// for await: required for babel to crash | ||
console.log(string); | ||
} | ||
}; | ||
|
||
const [one] = [1]; // array destructuring: required for babel to crash | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"plugins": [ | ||
"transform-destructuring", | ||
"transform-regenerator", | ||
"proposal-async-generator-functions" | ||
], | ||
"externalHelpers": false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
function _asyncIterator(iterable) { var method; if (typeof Symbol !== "undefined") { if (Symbol.asyncIterator) method = iterable[Symbol.asyncIterator]; if (method == null && Symbol.iterator) method = iterable[Symbol.iterator]; } if (method == null) method = iterable["@@asyncIterator"]; if (method == null) method = iterable["@@iterator"]; if (method == null) throw new TypeError("Object is not async iterable"); return method.call(iterable); } | ||
|
||
function main() { | ||
var one; | ||
return regeneratorRuntime.async(function main$(_context2) { | ||
while (1) switch (_context2.prev = _context2.next) { | ||
case 0: | ||
() => { | ||
var _iteratorAbruptCompletion, _didIteratorError, _iteratorError, _iterator, _step, string; | ||
|
||
return regeneratorRuntime.async(function _callee$(_context) { | ||
while (1) switch (_context.prev = _context.next) { | ||
case 0: | ||
// IIFE: required for babel to crash | ||
_iteratorAbruptCompletion = false; | ||
_didIteratorError = false; | ||
_context.prev = 2; | ||
_iterator = _asyncIterator(async_iterable); | ||
|
||
case 4: | ||
_context.next = 6; | ||
return regeneratorRuntime.awrap(_iterator.next()); | ||
|
||
case 6: | ||
if (!(_iteratorAbruptCompletion = !(_step = _context.sent).done)) { | ||
_context.next = 12; | ||
break; | ||
} | ||
|
||
string = _step.value; | ||
// for await: required for babel to crash | ||
console.log(string); | ||
|
||
case 9: | ||
_iteratorAbruptCompletion = false; | ||
_context.next = 4; | ||
break; | ||
|
||
case 12: | ||
_context.next = 18; | ||
break; | ||
|
||
case 14: | ||
_context.prev = 14; | ||
_context.t0 = _context["catch"](2); | ||
_didIteratorError = true; | ||
_iteratorError = _context.t0; | ||
|
||
case 18: | ||
_context.prev = 18; | ||
_context.prev = 19; | ||
|
||
if (!(_iteratorAbruptCompletion && _iterator.return != null)) { | ||
_context.next = 23; | ||
break; | ||
} | ||
|
||
_context.next = 23; | ||
return regeneratorRuntime.awrap(_iterator.return()); | ||
|
||
case 23: | ||
_context.prev = 23; | ||
|
||
if (!_didIteratorError) { | ||
_context.next = 26; | ||
break; | ||
} | ||
|
||
throw _iteratorError; | ||
|
||
case 26: | ||
return _context.finish(23); | ||
|
||
case 27: | ||
return _context.finish(18); | ||
|
||
case 28: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
}, null, null, [[2, 14, 18, 28], [19,, 23, 27]], Promise); | ||
}; | ||
|
||
one = 1; // array destructuring: required for babel to crash | ||
|
||
case 2: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
}, null, null, null, Promise); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,6 +61,14 @@ export function isDenylisted(this: NodePath): boolean { | |
// TODO: Remove in Babel 8 | ||
export { isDenylisted as isBlacklisted }; | ||
|
||
function restoreContext(path: NodePath, context: TraversalContext) { | ||
if (path.context !== context) { | ||
path.context = context; | ||
path.state = context.state; | ||
path.opts = context.opts; | ||
} | ||
} | ||
|
||
export function visit(this: NodePath): boolean { | ||
if (!this.node) { | ||
return false; | ||
|
@@ -74,15 +82,17 @@ export function visit(this: NodePath): boolean { | |
return false; | ||
} | ||
|
||
// Note: We need to check "this.shouldSkip" twice because | ||
// the visitor can set it to true. Usually .shouldSkip is false | ||
const currentContext = this.context; | ||
// Note: We need to check "this.shouldSkip" first because | ||
// another visitor can set it to true. Usually .shouldSkip is false | ||
// before calling the enter visitor, but it can be true in case of | ||
// a requeued node (e.g. by .replaceWith()) that is then marked | ||
// with .skip(). | ||
if (this.shouldSkip || this.call("enter") || this.shouldSkip) { | ||
if (this.shouldSkip || this.call("enter")) { | ||
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. If |
||
this.debug("Skip..."); | ||
return this.shouldStop; | ||
} | ||
restoreContext(this, currentContext); | ||
|
||
this.debug("Recursing into..."); | ||
traverse.node( | ||
|
@@ -94,6 +104,8 @@ export function visit(this: NodePath): boolean { | |
this.skipKeys, | ||
); | ||
|
||
restoreContext(this, currentContext); | ||
|
||
this.call("exit"); | ||
|
||
return this.shouldStop; | ||
|
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.
Is it intentional that this function is no longer invoked?
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.
Kind of. Personally I prefer the minimal AST structure that can reproduce the bug. In this case we are not testing the runtime semantics so the simpler the input code is, the easier to reason about what is going on. You can verify that the original code example also works on the PR REPL.
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.
Very epic thanks for explaining