feat(transformer): add async_generator_functions plugin (#6573)

Passed 15/19 tests. The remaining 4 failed tests related to `this` expression, the problem same as I mentioned in #6658. I will fix them in follow-up PRs.
This commit is contained in:
Dunqing 2024-10-31 09:14:17 +00:00
parent af5140fc3d
commit 934cb5e746
13 changed files with 870 additions and 11 deletions

View file

@ -137,15 +137,23 @@ fn default_as_module_name() -> Cow<'static, str> {
/// Available helpers.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Helper {
AwaitAsyncGenerator,
AsyncGeneratorDelegate,
AsyncIterator,
AsyncToGenerator,
ObjectSpread2,
WrapAsyncGenerator,
}
impl Helper {
const fn name(self) -> &'static str {
match self {
Self::AwaitAsyncGenerator => "awaitAsyncGenerator",
Self::AsyncGeneratorDelegate => "asyncGeneratorDelegate",
Self::AsyncIterator => "asyncIterator",
Self::AsyncToGenerator => "asyncToGenerator",
Self::ObjectSpread2 => "objectSpread2",
Self::WrapAsyncGenerator => "wrapAsyncGenerator",
}
}
}

View file

@ -104,7 +104,6 @@ impl<'a> StatementInjectorStore<'a> {
}
/// Add multiple statements to be inserted immediately before the target statement.
#[expect(dead_code)]
#[inline]
pub fn insert_many_before<A, S>(&self, target: &A, stmts: S)
where

View file

@ -696,6 +696,15 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> {
) {
BindingMover::new(target_scope_id, ctx).visit_binding_identifier(ident);
}
#[inline]
pub fn move_bindings_to_target_scope_for_statement(
target_scope_id: ScopeId,
stmt: &Statement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
BindingMover::new(target_scope_id, ctx).visit_statement(stmt);
}
}
/// Moves the bindings from original scope to target scope.

View file

@ -6,7 +6,6 @@ use oxc_ast::ast::{Expression, Statement};
use oxc_traverse::{Traverse, TraverseCtx};
use crate::{es2017::async_to_generator::AsyncToGenerator, TransformCtx};
#[expect(unused_imports)]
pub use async_to_generator::AsyncGeneratorExecutor;
#[allow(dead_code)]

View file

@ -0,0 +1,426 @@
//! This module is responsible for transforming `for await` to `for` statement
use oxc_allocator::Vec;
use oxc_ast::{ast::*, NONE};
use oxc_semantic::{ScopeFlags, ScopeId, SymbolFlags};
use oxc_span::SPAN;
use oxc_traverse::{BoundIdentifier, TraverseCtx};
use super::AsyncGeneratorFunctions;
use crate::{common::helper_loader::Helper, es2017::AsyncGeneratorExecutor};
impl<'a, 'ctx> AsyncGeneratorFunctions<'a, 'ctx> {
pub(crate) fn transform_for_of_statement(
&mut self,
stmt: &mut ForOfStatement<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Vec<'a, Statement<'a>> {
let step_key =
ctx.generate_uid("step", ctx.current_scope_id(), SymbolFlags::FunctionScopedVariable);
// step.value
let step_value = ctx.ast.expression_member(ctx.ast.member_expression_static(
SPAN,
step_key.create_read_expression(ctx),
ctx.ast.identifier_name(SPAN, "value"),
false,
));
let assignment_statement = match &mut stmt.left {
ForStatementLeft::VariableDeclaration(variable) => {
// for await (let i of test)
let mut declarator = variable.declarations.pop().unwrap();
declarator.init = Some(step_value);
let variable = ctx.ast.variable_declaration(
SPAN,
declarator.kind,
ctx.ast.vec1(declarator),
false,
);
let declaration = ctx.ast.declaration_from_variable(variable);
Statement::from(declaration)
}
left @ match_assignment_target!(ForStatementLeft) => {
// for await (i of test), for await ({ i } of test)
let target = ctx.ast.move_assignment_target(left.to_assignment_target_mut());
let expression = ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
target,
step_value,
);
ctx.ast.statement_expression(SPAN, expression)
}
};
let body = {
let mut statements = ctx.ast.vec_with_capacity(2);
statements.push(assignment_statement);
let stmt_body = &mut stmt.body;
if let Statement::BlockStatement(block) = stmt_body {
if block.body.is_empty() {
// If the block is empty, we dont need to add it to the body;
// instead, we need to remove the useless scope.
ctx.scopes_mut().delete_scope(block.scope_id.get().unwrap());
} else {
statements.push(ctx.ast.move_statement(stmt_body));
}
}
statements
};
Self::build_for_await(
self.ctx.helper_load(Helper::AsyncIterator, ctx),
ctx.ast.move_expression(&mut stmt.right),
&step_key,
body,
stmt.scope_id.get().unwrap(),
ctx,
)
}
/// Build a `for` statement used to replace the `for await` statement.
///
/// This function builds the following code:
///
/// ```js
// var ITERATOR_ABRUPT_COMPLETION = false;
// var ITERATOR_HAD_ERROR_KEY = false;
// var ITERATOR_ERROR_KEY;
// try {
// for (
// var ITERATOR_KEY = GET_ITERATOR(OBJECT), STEP_KEY;
// ITERATOR_ABRUPT_COMPLETION = !(STEP_KEY = await ITERATOR_KEY.next()).done;
// ITERATOR_ABRUPT_COMPLETION = false
// ) {
// }
// } catch (err) {
// ITERATOR_HAD_ERROR_KEY = true;
// ITERATOR_ERROR_KEY = err;
// } finally {
// try {
// if (ITERATOR_ABRUPT_COMPLETION && ITERATOR_KEY.return != null) {
// await ITERATOR_KEY.return();
// }
// } finally {
// if (ITERATOR_HAD_ERROR_KEY) {
// throw ITERATOR_ERROR_KEY;
// }
// }
// }
/// ```
///
/// Based on Babel's implementation:
/// <https://github.com/babel/babel/blob/d20b314c14533ab86351ecf6ca6b7296b66a57b3/packages/babel-plugin-transform-async-generator-functions/src/for-await.ts#L3-L30>
fn build_for_await(
get_identifier: Expression<'a>,
object: Expression<'a>,
step_key: &BoundIdentifier<'a>,
body: Vec<'a, Statement<'a>>,
for_of_scope_id: ScopeId,
ctx: &mut TraverseCtx<'a>,
) -> Vec<'a, Statement<'a>> {
let scope_id = ctx.current_scope_id();
let iterator_had_error_key =
ctx.generate_uid("didIteratorError", scope_id, SymbolFlags::FunctionScopedVariable);
let iterator_abrupt_completion = ctx.generate_uid(
"iteratorAbruptCompletion",
scope_id,
SymbolFlags::FunctionScopedVariable,
);
let iterator_error_key =
ctx.generate_uid("iteratorError", scope_id, SymbolFlags::FunctionScopedVariable);
let mut items = ctx.ast.vec_with_capacity(4);
items.push(ctx.ast.statement_declaration(ctx.ast.declaration_variable(
SPAN,
VariableDeclarationKind::Var,
ctx.ast.vec1(ctx.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Var,
iterator_abrupt_completion.create_binding_pattern(ctx),
Some(ctx.ast.expression_boolean_literal(SPAN, false)),
false,
)),
false,
)));
items.push(ctx.ast.statement_declaration(ctx.ast.declaration_variable(
SPAN,
VariableDeclarationKind::Var,
ctx.ast.vec1(ctx.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Var,
iterator_had_error_key.create_binding_pattern(ctx),
Some(ctx.ast.expression_boolean_literal(SPAN, false)),
false,
)),
false,
)));
items.push(ctx.ast.statement_declaration(ctx.ast.declaration_variable(
SPAN,
VariableDeclarationKind::Var,
ctx.ast.vec1(ctx.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Var,
iterator_error_key.create_binding_pattern(ctx),
None,
false,
)),
false,
)));
let iterator_key =
ctx.generate_uid("iterator", scope_id, SymbolFlags::FunctionScopedVariable);
let block = {
let block_scope_id = ctx.create_child_scope(scope_id, ScopeFlags::empty());
let for_statement_scope_id =
ctx.create_child_scope(block_scope_id, ScopeFlags::empty());
ctx.scopes_mut().change_parent_id(for_of_scope_id, Some(block_scope_id));
let for_statement = ctx.ast.for_statement_with_scope_id(
SPAN,
Some(ctx.ast.for_statement_init_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
{
let mut items = ctx.ast.vec_with_capacity(2);
items.push(ctx.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Var,
iterator_key.create_binding_pattern(ctx),
Some(ctx.ast.expression_call(
SPAN,
get_identifier,
NONE,
ctx.ast.vec1(ctx.ast.argument_expression(object)),
false,
)),
false,
));
items.push(ctx.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Var,
step_key.create_binding_pattern(ctx),
None,
false,
));
items
},
false,
)),
Some(ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
iterator_abrupt_completion.create_read_write_target(ctx),
ctx.ast.expression_unary(
SPAN,
UnaryOperator::LogicalNot,
ctx.ast.expression_member(ctx.ast.member_expression_static(
SPAN,
ctx.ast.expression_parenthesized(
SPAN,
ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
step_key.create_read_write_target(ctx),
ctx.ast.expression_await(
SPAN,
ctx.ast.expression_call(
SPAN,
ctx.ast.expression_member(
ctx.ast.member_expression_static(
SPAN,
iterator_key.create_read_expression(ctx),
ctx.ast.identifier_name(SPAN, "next"),
false,
),
),
NONE,
ctx.ast.vec(),
false,
),
),
),
),
ctx.ast.identifier_name(SPAN, "done"),
false,
)),
),
)),
Some(ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
iterator_abrupt_completion.create_read_write_target(ctx),
ctx.ast.expression_boolean_literal(SPAN, false),
)),
{
// Handle the for-of statement move to the body of new for-statement
let for_statement_body_scope_id = for_of_scope_id;
{
ctx.scopes_mut().change_parent_id(
for_statement_body_scope_id,
Some(for_statement_scope_id),
);
let statement = body.first().unwrap();
AsyncGeneratorExecutor::move_bindings_to_target_scope_for_statement(
for_statement_body_scope_id,
statement,
ctx,
);
}
ctx.ast.statement_from_block(ctx.ast.block_statement_with_scope_id(
SPAN,
body,
for_statement_body_scope_id,
))
},
for_statement_scope_id,
);
let statement = ctx.ast.statement_from_for(for_statement);
ctx.ast.block_statement_with_scope_id(SPAN, ctx.ast.vec1(statement), block_scope_id)
};
let catch_clause = {
let catch_scope_id = ctx.create_child_scope(scope_id, ScopeFlags::CatchClause);
let block_scope_id = ctx.create_child_scope(catch_scope_id, ScopeFlags::empty());
let err_ident = ctx.generate_binding(
Atom::from("err"),
block_scope_id,
SymbolFlags::CatchVariable | SymbolFlags::FunctionScopedVariable,
);
Some(ctx.ast.catch_clause_with_scope_id(
SPAN,
Some(ctx.ast.catch_parameter(SPAN, err_ident.create_binding_pattern(ctx))),
{
ctx.ast.block_statement_with_scope_id(
SPAN,
{
let mut items = ctx.ast.vec_with_capacity(2);
items.push(ctx.ast.statement_expression(
SPAN,
ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
iterator_had_error_key.create_write_target(ctx),
ctx.ast.expression_boolean_literal(SPAN, true),
),
));
items.push(ctx.ast.statement_expression(
SPAN,
ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
iterator_error_key.create_write_target(ctx),
err_ident.create_read_expression(ctx),
),
));
items
},
block_scope_id,
)
},
catch_scope_id,
))
};
let finally = {
let finally_scope_id = ctx.create_child_scope(scope_id, ScopeFlags::empty());
let try_statement = {
let try_block_scope_id =
ctx.create_child_scope(finally_scope_id, ScopeFlags::empty());
let if_statement = {
let if_block_scope_id =
ctx.create_child_scope(try_block_scope_id, ScopeFlags::empty());
ctx.ast.statement_if(
SPAN,
ctx.ast.expression_logical(
SPAN,
iterator_abrupt_completion.create_read_expression(ctx),
LogicalOperator::And,
ctx.ast.expression_binary(
SPAN,
ctx.ast.expression_member(ctx.ast.member_expression_static(
SPAN,
iterator_key.create_read_expression(ctx),
ctx.ast.identifier_name(SPAN, "return"),
false,
)),
BinaryOperator::Inequality,
ctx.ast.expression_null_literal(SPAN),
),
),
ctx.ast.statement_from_block(ctx.ast.block_statement_with_scope_id(
SPAN,
ctx.ast.vec1(ctx.ast.statement_expression(
SPAN,
ctx.ast.expression_await(
SPAN,
ctx.ast.expression_call(
SPAN,
ctx.ast.expression_member(
ctx.ast.member_expression_static(
SPAN,
iterator_key.create_read_expression(ctx),
ctx.ast.identifier_name(SPAN, "return"),
false,
),
),
NONE,
ctx.ast.vec(),
false,
),
),
)),
if_block_scope_id,
)),
None,
)
};
let block = ctx.ast.block_statement_with_scope_id(
SPAN,
ctx.ast.vec1(if_statement),
try_block_scope_id,
);
let finally = {
let finally_scope_id =
ctx.create_child_scope(finally_scope_id, ScopeFlags::empty());
let if_statement = {
let if_block_scope_id =
ctx.create_child_scope(finally_scope_id, ScopeFlags::empty());
ctx.ast.statement_if(
SPAN,
iterator_had_error_key.create_read_expression(ctx),
ctx.ast.statement_from_block(ctx.ast.block_statement_with_scope_id(
SPAN,
ctx.ast.vec1(ctx.ast.statement_throw(
SPAN,
iterator_error_key.create_read_expression(ctx),
)),
if_block_scope_id,
)),
None,
)
};
ctx.ast.block_statement_with_scope_id(
SPAN,
ctx.ast.vec1(if_statement),
finally_scope_id,
)
};
ctx.ast.statement_try(SPAN, block, NONE, Some(finally))
};
let block_statement = ctx.ast.block_statement_with_scope_id(
SPAN,
ctx.ast.vec1(try_statement),
finally_scope_id,
);
Some(block_statement)
};
let try_statement = ctx.ast.statement_try(SPAN, block, catch_clause, finally);
items.push(try_statement);
items
}
}

View file

@ -0,0 +1,256 @@
//! ES2018: Async Generator Functions
//!
//! This plugin mainly does the following transformations:
//!
//! 1. transforms async generator functions (async function *name() {}) to generator functions
//! and wraps them with `awaitAsyncGenerator` helper function.
//! 2. transforms `await expr` expression to `yield awaitAsyncGenerator(expr)`.
//! 3. transforms `yield * argument` expression to `yield asyncGeneratorDelegate(asyncIterator(argument))`.
//! 4. transforms `for await` statement to `for` statement, and inserts many code to handle async iteration.
//!
//! ## Example
//!
//! Input:
//! ```js
//! async function f() {
//! for await (let x of y) {
//! g(x);
//! }
//!}
//! ```
//!
//! Output:
//! ```js
//! function f() {
//! return _f.apply(this, arguments);
//! }
//! function _f() {
//! _f = babelHelpers.asyncToGenerator(function* () {
//! var _iteratorAbruptCompletion = false;
//! var _didIteratorError = false;
//! var _iteratorError;
//! try {
//! for (var _iterator = babelHelpers.asyncIterator(y), _step; _iteratorAbruptCompletion = !(_step = yield _iterator.next()).done; _iteratorAbruptCompletion = false) {
//! let x = _step.value;
//! {
//! g(x);
//! }
//! }
//! } catch (err) {
//! _didIteratorError = true;
//! _iteratorError = err;
//! } finally {
//! try {
//! if (_iteratorAbruptCompletion && _iterator.return != null) {
//! yield _iterator.return();
//! }
//! } finally {
//! if (_didIteratorError) {
//! throw _iteratorError;
//! }
//! }
//! }
//! });
//! return _f.apply(this, arguments);
//! }
//! ```
//!
//! ## Implementation
//!
//! Implementation based on [@babel/plugin-transform-async-generator-functions](https://babel.dev/docs/babel-plugin-transform-async-generator-functions).
//!
//! Reference:
//! * Babel docs: <https://babeljs.io/docs/en/babel-plugin-transform-async-generator-functions>
//! * Babel implementation: <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-async-generator-functions>
//! * Async Iteration TC39 proposal: <https://github.com/tc39/proposal-async-iteration>
mod for_await;
use oxc_allocator::GetAddress;
use oxc_ast::ast::*;
use oxc_data_structures::stack::Stack;
use oxc_span::SPAN;
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
use crate::{common::helper_loader::Helper, context::TransformCtx, es2017::AsyncGeneratorExecutor};
pub struct AsyncGeneratorFunctions<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,
stack: Stack<bool>,
executor: AsyncGeneratorExecutor<'a, 'ctx>,
}
impl<'a, 'ctx> AsyncGeneratorFunctions<'a, 'ctx> {
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
Self {
ctx,
executor: AsyncGeneratorExecutor::new(Helper::WrapAsyncGenerator, ctx),
stack: Stack::new(),
}
}
}
impl<'a, 'ctx> Traverse<'a> for AsyncGeneratorFunctions<'a, 'ctx> {
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>) {
if let Statement::ForOfStatement(for_of) = stmt {
if !for_of.r#await {
return;
}
// We need to replace the current statement with new statements,
// but we don't have a such method to do it, so we leverage the statement injector.
//
// Now, we use below steps to workaround it:
// 1. Use the last statement as the new statement.
// 2. insert the rest of the statements before the current statement.
// TODO: Once we have a method to replace the current statement, we can simplify this logic.
let mut statements = self.transform_for_of_statement(for_of, ctx);
let last_statement = statements.pop().unwrap();
*stmt = last_statement;
self.ctx.statement_injector.insert_many_before(&stmt.address(), statements);
}
}
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 {
if function.r#async && function.generator && !function.is_typescript_syntax() {
let new_statement = self.executor.transform_function_declaration(function, ctx);
self.ctx.statement_injector.insert_after(stmt, new_statement);
}
}
}
fn exit_method_definition(
&mut self,
node: &mut MethodDefinition<'a>,
ctx: &mut TraverseCtx<'a>,
) {
let function = &mut node.value;
if function.r#async && function.generator && !function.is_typescript_syntax() {
self.executor.transform_function_for_method_definition(function, ctx);
}
}
fn enter_function(&mut self, func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
self.stack.push(func.r#async && func.generator);
}
fn exit_function(&mut self, _func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
self.stack.pop();
}
}
impl<'a, 'ctx> AsyncGeneratorFunctions<'a, 'ctx> {
/// Transform `yield * argument` expression to `yield asyncGeneratorDelegate(asyncIterator(argument))`.
#[allow(clippy::unused_self)]
fn transform_yield_expression(
&self,
expr: &mut YieldExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
if !expr.delegate {
return None;
}
expr.argument.as_mut().map(|argument| {
let argument = Argument::from(ctx.ast.move_expression(argument));
let arguments = ctx.ast.vec1(argument);
let mut argument = self.ctx.helper_call_expr(Helper::AsyncIterator, arguments, ctx);
let arguments = ctx.ast.vec1(Argument::from(argument));
argument = self.ctx.helper_call_expr(Helper::AsyncGeneratorDelegate, arguments, ctx);
ctx.ast.expression_yield(SPAN, expr.delegate, Some(argument))
})
}
/// Transforms `await expr` expression to `yield awaitAsyncGenerator(expr)`.
/// Ignores top-level await expression.
#[allow(clippy::unused_self)]
fn transform_await_expression(
&self,
expr: &mut AwaitExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
// We don't need to handle top-level await.
if ctx.parent().is_program() ||
// Check the function is async generator function
!self.stack.last().copied().unwrap_or(false)
{
return None;
}
let mut is_function = false;
let mut function_in_params = false;
for ancestor in ctx.ancestors() {
match ancestor {
Ancestor::FunctionBody(_) if !is_function => {
is_function = true;
}
// x = async function() { await 1 }
Ancestor::AssignmentPatternRight(_) | Ancestor::BindingPatternKind(_) => {
continue;
}
Ancestor::FormalParameterPattern(_) => {
function_in_params = true;
break;
}
_ => {
if is_function {
break;
}
}
}
}
let mut argument = ctx.ast.move_expression(&mut expr.argument);
// When a async function is used as parameter, we don't need to wrap its await expression with awaitAsyncGenerator helper.
// `function example(a = async function b() { await 1 }) {}`
if !function_in_params {
let arguments = ctx.ast.vec1(Argument::from(argument));
argument = self.ctx.helper_call_expr(Helper::AwaitAsyncGenerator, arguments, ctx);
}
Some(ctx.ast.expression_yield(SPAN, false, Some(argument)))
}
}

View file

@ -1,11 +1,12 @@
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};
use crate::TransformCtx;
mod async_generator_functions;
mod object_rest_spread;
mod options;
use oxc_ast::ast::{Expression, ForOfStatement, Function, MethodDefinition, Statement};
use oxc_traverse::{Traverse, TraverseCtx};
use crate::context::TransformCtx;
use async_generator_functions::AsyncGeneratorFunctions;
pub use object_rest_spread::{ObjectRestSpread, ObjectRestSpreadOptions};
pub use options::ES2018Options;
@ -14,6 +15,7 @@ pub struct ES2018<'a, 'ctx> {
// Plugins
object_rest_spread: ObjectRestSpread<'a, 'ctx>,
async_generator_functions: AsyncGeneratorFunctions<'a, 'ctx>,
}
impl<'a, 'ctx> ES2018<'a, 'ctx> {
@ -23,6 +25,7 @@ impl<'a, 'ctx> ES2018<'a, 'ctx> {
options.object_rest_spread.unwrap_or_default(),
ctx,
),
async_generator_functions: AsyncGeneratorFunctions::new(ctx),
options,
}
}
@ -34,4 +37,50 @@ impl<'a, 'ctx> Traverse<'a> for ES2018<'a, 'ctx> {
self.object_rest_spread.enter_expression(expr, ctx);
}
}
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.async_generator_functions {
self.async_generator_functions.exit_expression(expr, ctx);
}
}
fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.async_generator_functions {
self.async_generator_functions.enter_statement(stmt, ctx);
}
}
fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.async_generator_functions {
self.async_generator_functions.exit_statement(stmt, ctx);
}
}
fn exit_method_definition(
&mut self,
node: &mut MethodDefinition<'a>,
ctx: &mut TraverseCtx<'a>,
) {
if self.options.async_generator_functions {
self.async_generator_functions.exit_method_definition(node, ctx);
}
}
fn enter_for_of_statement(&mut self, node: &mut ForOfStatement<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.async_generator_functions {
self.async_generator_functions.enter_for_of_statement(node, ctx);
}
}
fn enter_function(&mut self, node: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.async_generator_functions {
self.async_generator_functions.enter_function(node, ctx);
}
}
fn exit_function(&mut self, node: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.async_generator_functions {
self.async_generator_functions.exit_function(node, ctx);
}
}
}

View file

@ -7,4 +7,6 @@ use super::ObjectRestSpreadOptions;
pub struct ES2018Options {
#[serde(skip)]
pub object_rest_spread: Option<ObjectRestSpreadOptions>,
#[serde(skip)]
pub async_generator_functions: bool,
}

View file

@ -227,6 +227,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x1_jsx.exit_expression(expr, ctx);
self.x2_es2018.exit_expression(expr, ctx);
self.x2_es2017.exit_expression(expr, ctx);
self.x3_es2015.exit_expression(expr, ctx);
}
@ -262,6 +263,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
}
fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
self.x2_es2018.enter_function(func, ctx);
self.x3_es2015.enter_function(func, ctx);
}
@ -270,6 +272,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
typescript.exit_function(func, ctx);
}
self.x1_jsx.exit_function(func, ctx);
self.x2_es2018.exit_function(func, ctx);
self.x3_es2015.exit_function(func, ctx);
}
@ -326,6 +329,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
if let Some(typescript) = self.x0_typescript.as_mut() {
typescript.exit_method_definition(def, ctx);
}
self.x2_es2018.exit_method_definition(def, ctx);
self.x2_es2017.exit_method_definition(def, ctx);
}
@ -405,6 +409,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
if let Some(typescript) = self.x0_typescript.as_mut() {
typescript.exit_statement(stmt, ctx);
}
self.x2_es2018.exit_statement(stmt, ctx);
self.x2_es2017.exit_statement(stmt, ctx);
}
@ -422,6 +427,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
if let Some(typescript) = self.x0_typescript.as_mut() {
typescript.enter_statement(stmt, ctx);
}
self.x2_es2018.enter_statement(stmt, ctx);
}
fn enter_declaration(&mut self, decl: &mut Declaration<'a>, ctx: &mut TraverseCtx<'a>) {
@ -462,6 +468,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
if let Some(typescript) = self.x0_typescript.as_mut() {
typescript.enter_for_of_statement(stmt, ctx);
}
self.x2_es2018.enter_for_of_statement(stmt, ctx);
}
fn enter_for_in_statement(&mut self, stmt: &mut ForInStatement<'a>, ctx: &mut TraverseCtx<'a>) {

View file

@ -101,7 +101,11 @@ impl TransformOptions {
// Turned off because it is not ready.
async_to_generator: false,
},
es2018: ES2018Options { object_rest_spread: Some(ObjectRestSpreadOptions::default()) },
es2018: ES2018Options {
object_rest_spread: Some(ObjectRestSpreadOptions::default()),
// Turned off because it is not ready.
async_generator_functions: false,
},
es2019: ES2019Options { optional_catch_binding: true },
es2020: ES2020Options { nullish_coalescing_operator: true },
es2021: ES2021Options { logical_assignment_operators: true },
@ -145,6 +149,8 @@ impl TryFrom<&EnvOptions> for TransformOptions {
object_rest_spread: o
.can_enable_plugin("transform-object-rest-spread")
.then(Default::default),
async_generator_functions: o
.can_enable_plugin("transform-async-generator-functions"),
},
es2019: ES2019Options {
optional_catch_binding: o.can_enable_plugin("transform-optional-catch-binding"),
@ -314,6 +320,10 @@ impl TryFrom<&BabelOptions> for TransformOptions {
})
.or(env.es2018.object_rest_spread)
},
async_generator_functions: {
let plugin_name = "transform-async-generator-functions";
options.get_plugin(plugin_name).is_some() || env.es2018.async_generator_functions
},
};
let es2019 = ES2019Options {

View file

@ -1,6 +1,6 @@
commit: d20b314c
Passed: 361/1058
Passed: 376/1077
# All Passed:
* babel-plugin-transform-class-static-block
@ -1450,6 +1450,20 @@ x Output mismatch
x Output mismatch
# babel-plugin-transform-async-generator-functions (15/19)
* async-generators/class-method/input.js
x Output mismatch
* async-generators/object-method/input.js
x Output mismatch
* async-generators/static-method/input.js
x Output mismatch
* nested/arrows-in-declaration/input.js
x Output mismatch
# babel-plugin-transform-object-rest-spread (5/59)
* assumption-ignoreFunctionLength/parameters-object-rest-used-in-default/input.js
x Output mismatch

View file

@ -1,6 +1,6 @@
commit: d20b314c
Passed: 45/73
Passed: 45/99
# All Passed:
* babel-plugin-transform-class-static-block
@ -24,6 +24,86 @@ exec failed
exec failed
# babel-plugin-transform-async-generator-functions (0/26)
* async-generators/declaration-exec/exec.js
exec failed
* async-generators/yield-exec/exec.js
exec failed
* for-await/async-generator-exec/exec.js
exec failed
* for-await/create-async-from-sync-iterator/exec.js
exec failed
* for-await/lhs-member-expression/exec.js
exec failed
* for-await/re-declare-var-in-init-body/exec.js
exec failed
* for-await/step-single-tick/exec.js
exec failed
* for-await/step-value-is-promise/exec.js
exec failed
* for-await/step-value-not-accessed-when-done/exec.js
exec failed
* regression/test262-fn-length/exec.js
exec failed
* yield-star/create-async-from-sync-iterator/exec.js
exec failed
* yield-star/ecma262-pr-2819/exec.js
exec failed
* yield-star/issue-9905/exec.js
exec failed
* yield-star/return-method/exec.js
exec failed
* yield-star/return-method-with-finally/exec.js
exec failed
* yield-star/return-method-with-finally-multiple-parallel/exec.js
exec failed
* yield-star/return-method-with-finally-multiple-serial/exec.js
exec failed
* yield-star/throw-method-with-catch/exec.js
exec failed
* yield-star/throw-method-with-finally/exec.js
exec failed
* yield-star-polyfill-corejs3/issue-9905/exec.js
exec failed
* yield-star-polyfill-corejs3/return-method/exec.js
exec failed
* yield-star-polyfill-corejs3/return-method-with-finally/exec.js
exec failed
* yield-star-polyfill-corejs3/return-method-with-finally-multiple-parallel/exec.js
exec failed
* yield-star-polyfill-corejs3/return-method-with-finally-multiple-serial/exec.js
exec failed
* yield-star-polyfill-corejs3/throw-method-with-catch/exec.js
exec failed
* yield-star-polyfill-corejs3/throw-method-with-finally/exec.js
exec failed
# babel-plugin-transform-object-rest-spread (15/31)
* assumption-objectRestNoSymbols/rest-ignore-symbols/exec.js
exec failed

View file

@ -23,7 +23,7 @@ pub(crate) const PLUGINS: &[&str] = &[
"babel-plugin-transform-optional-catch-binding",
// "babel-plugin-transform-json-strings",
// // ES2018
// "babel-plugin-transform-async-generator-functions",
"babel-plugin-transform-async-generator-functions",
"babel-plugin-transform-object-rest-spread",
// // [Regex] "babel-plugin-transform-unicode-property-regex",
// "babel-plugin-transform-dotall-regex",