Skip to content

Commit

Permalink
refactor(transformer/async-generator-functions): simplify identifying…
Browse files Browse the repository at this point in the history
… whether within an async generator function (#7170)

Follow-on after stack up to #7148.

Split `is_inside_async_generator_function` into 2 functions `yield_is_inside_async_generator_function` and `async_is_inside_async_generator_function`.

There are a couple of differences between `yield` and `await`, which we can leverage to make both these functions simpler:

* `yield` cannot appear at top level (there's no such thing as "top level yield").
* `yield` cannot appear in an arrow function (there's no such thing as a "generator arrow function").

In addition:

* Because each function now handles a specific case, no need to check if function containing `async` is an async function. It must be. Ditto for `yield`.
* Remove the `if ctx.current_scope_flags().is_top()` check. Probably this check costs more than it saves because top level `await` is far less common than `await` within async functions. So this check was optimizing for an uncommon case, at a cost to the common case.
* Add more comments to explain the logic.

This is all quite small optimizations, but I was having difficulty understanding the logic, and found that splitting it up made it clearer (at least for me).
  • Loading branch information
overlookmotel committed Nov 6, 2024
1 parent 64b7e3a commit 84ee581
Showing 1 changed file with 34 additions and 21 deletions.
55 changes: 34 additions & 21 deletions crates/oxc_transformer/src/es2018/async_generator_functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,32 +157,13 @@ impl<'a, 'ctx> Traverse<'a> for AsyncGeneratorFunctions<'a, 'ctx> {
}

impl<'a, 'ctx> AsyncGeneratorFunctions<'a, 'ctx> {
/// Check whether the current node is inside an async generator function.
fn is_inside_async_generator_function(ctx: &mut TraverseCtx<'a>) -> bool {
// Early return if current scope is top because we don't need to transform top-level await expression.
if ctx.current_scope_flags().is_top() {
return false;
}

for ancestor in ctx.ancestors() {
match ancestor {
Ancestor::FunctionBody(func) => return *func.r#async() && *func.generator(),
Ancestor::ArrowFunctionExpressionBody(_) => {
return false;
}
_ => {}
}
}
false
}

/// Transform `yield * argument` expression to `yield asyncGeneratorDelegate(asyncIterator(argument))`.
fn transform_yield_expression(
&self,
expr: &mut YieldExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
if !expr.delegate || !Self::is_inside_async_generator_function(ctx) {
if !expr.delegate || !Self::yield_is_inside_async_generator_function(ctx) {
return None;
}

Expand All @@ -196,14 +177,27 @@ impl<'a, 'ctx> AsyncGeneratorFunctions<'a, 'ctx> {
})
}

/// Check whether `yield` is inside an async generator function.
fn yield_is_inside_async_generator_function(ctx: &TraverseCtx<'a>) -> bool {
for ancestor in ctx.ancestors() {
// Note: `yield` cannot appear within function params.
// Also cannot appear in arrow functions because no such thing as a generator arrow function.
if let Ancestor::FunctionBody(func) = ancestor {
return *func.r#async();
}
}
// `yield` can only appear in a function
unreachable!();
}

/// Transforms `await expr` expression to `yield awaitAsyncGenerator(expr)`.
/// Ignores top-level await expression.
fn transform_await_expression(
&self,
expr: &mut AwaitExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
if !Self::is_inside_async_generator_function(ctx) {
if !Self::async_is_inside_async_generator_function(ctx) {
return None;
}

Expand All @@ -213,4 +207,23 @@ impl<'a, 'ctx> AsyncGeneratorFunctions<'a, 'ctx> {

Some(ctx.ast.expression_yield(SPAN, false, Some(argument)))
}

/// Check whether `await` is inside an async generator function.
fn async_is_inside_async_generator_function(ctx: &TraverseCtx<'a>) -> bool {
for ancestor in ctx.ancestors() {
match ancestor {
// Note: `await` cannot appear within function params
Ancestor::FunctionBody(func) => {
return *func.generator();
}
Ancestor::ArrowFunctionExpressionBody(_) => {
// Arrow functions can't be generator functions
return false;
}
_ => {}
}
}
// Top level await
false
}
}

0 comments on commit 84ee581

Please sign in to comment.