mod for_await;
use oxc_allocator::TakeIn;
use oxc_ast::ast::*;
use oxc_span::SPAN;
use oxc_traverse::{Ancestor, Traverse};
use crate::{
common::helper_loader::{Helper, helper_call_expr},
context::TraverseCtx,
es2017::AsyncGeneratorExecutor,
state::TransformState,
};
pub struct AsyncGeneratorFunctions<'a> {
executor: AsyncGeneratorExecutor<'a>,
}
impl AsyncGeneratorFunctions<'_> {
pub fn new() -> Self {
Self { executor: AsyncGeneratorExecutor::new(Helper::WrapAsyncGenerator) }
}
}
impl<'a> Traverse<'a, TransformState<'a>> for AsyncGeneratorFunctions<'a> {
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
let new_expr = match expr {
Expression::AwaitExpression(await_expr) => {
self.transform_await_expression(await_expr, ctx)
}
Expression::YieldExpression(yield_expr) => {
self.transform_yield_expression(yield_expr, ctx)
}
Expression::FunctionExpression(func) => {
if func.r#async && func.generator {
Some(self.executor.transform_function_expression(func, ctx))
} else {
None
}
}
_ => None,
};
if let Some(new_expr) = new_expr {
*expr = new_expr;
}
}
fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
self.transform_statement(stmt, ctx);
}
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
let function = match stmt {
Statement::FunctionDeclaration(func) => Some(func),
Statement::ExportDefaultDeclaration(decl) => {
if let ExportDefaultDeclarationKind::FunctionDeclaration(func) =
&mut decl.declaration
{
Some(func)
} else {
None
}
}
Statement::ExportNamedDeclaration(decl) => {
if let Some(Declaration::FunctionDeclaration(func)) = &mut decl.declaration {
Some(func)
} else {
None
}
}
_ => None,
};
if let Some(function) = function
&& function.r#async
&& function.generator
&& !function.is_typescript_syntax()
{
let new_statement = self.executor.transform_function_declaration(function, ctx);
ctx.state.statement_injector.insert_after(stmt, new_statement);
}
}
fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
if func.r#async
&& func.generator
&& !func.is_typescript_syntax()
&& AsyncGeneratorExecutor::is_class_method_like_ancestor(ctx.parent())
{
self.executor.transform_function_for_method_definition(func, ctx);
}
}
}
impl<'a> AsyncGeneratorFunctions<'a> {
#[expect(clippy::unused_self)]
fn transform_yield_expression(
&self,
expr: &mut YieldExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
if !expr.delegate || !Self::yield_is_inside_async_generator_function(ctx) {
return None;
}
expr.argument.as_mut().map(|argument| {
let argument = Argument::from(argument.take_in(ctx.ast));
let arguments = ctx.ast.vec1(argument);
let mut argument = helper_call_expr(Helper::AsyncIterator, arguments, ctx);
let arguments = ctx.ast.vec1(Argument::from(argument));
argument = helper_call_expr(Helper::AsyncGeneratorDelegate, arguments, ctx);
ctx.ast.expression_yield(SPAN, expr.delegate, Some(argument))
})
}
fn yield_is_inside_async_generator_function(ctx: &TraverseCtx<'a>) -> bool {
for ancestor in ctx.ancestors() {
if let Ancestor::FunctionBody(func) = ancestor {
return *func.r#async();
}
}
unreachable!();
}
#[expect(clippy::unused_self)]
fn transform_await_expression(
&self,
expr: &mut AwaitExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
if !Self::async_is_inside_async_generator_function(ctx) {
return None;
}
let mut argument = expr.argument.take_in(ctx.ast);
let arguments = ctx.ast.vec1(Argument::from(argument));
argument = helper_call_expr(Helper::AwaitAsyncGenerator, arguments, ctx);
Some(ctx.ast.expression_yield(SPAN, false, Some(argument)))
}
fn async_is_inside_async_generator_function(ctx: &TraverseCtx<'a>) -> bool {
for ancestor in ctx.ancestors() {
match ancestor {
Ancestor::FunctionBody(func) => {
return *func.generator();
}
Ancestor::ArrowFunctionExpressionBody(_) => {
return false;
}
_ => {}
}
}
false
}
}