From 3e43a15855322003c5883c34f8cc9edb40bfdf83 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Fri, 18 Oct 2024 22:31:32 +0800 Subject: [PATCH] feat(transformer/async-to-generator): transform functions and arrow functions which have no id and no arguments --- crates/oxc_ast/src/ast_builder_impl.rs | 39 ++ crates/oxc_semantic/src/scope.rs | 18 + .../src/common/statement_injector.rs | 142 +----- .../src/es2017/async_to_generator.rs | 451 ++++++++++++++---- crates/oxc_transformer/src/lib.rs | 1 - .../snapshots/babel.snap.md | 19 +- 6 files changed, 416 insertions(+), 254 deletions(-) diff --git a/crates/oxc_ast/src/ast_builder_impl.rs b/crates/oxc_ast/src/ast_builder_impl.rs index 47d98436579ce0..e428056f0f3259 100644 --- a/crates/oxc_ast/src/ast_builder_impl.rs +++ b/crates/oxc_ast/src/ast_builder_impl.rs @@ -147,6 +147,45 @@ impl<'a> AstBuilder<'a> { mem::replace(decl, empty_decl) } + /// Move a formal parameters out by replacing it with an empty [FormalParameters]. + #[inline] + pub fn move_formal_parameters(self, params: &mut FormalParameters<'a>) -> FormalParameters<'a> { + let empty_params = self.formal_parameters(Span::default(), params.kind, self.vec(), NONE); + mem::replace(params, empty_params) + } + + /// Move a function out by replacing it with an empty [Function] + #[inline] + pub fn move_function(self, function: &mut Function<'a>) -> Function<'a> { + let params = self.formal_parameters( + Span::default(), + FormalParameterKind::FormalParameter, + self.vec(), + NONE, + ); + let empty_function = self.function( + FunctionType::FunctionDeclaration, + Span::default(), + None, + false, + false, + false, + NONE, + NONE, + params, + NONE, + NONE, + ); + mem::replace(function, empty_function) + } + + /// Move a function body out by replacing it with an empty [FunctionBody]. + #[inline] + pub fn move_function_body(self, body: &mut FunctionBody<'a>) -> FunctionBody<'a> { + let empty_body = self.function_body(Span::default(), self.vec(), self.vec()); + mem::replace(body, empty_body) + } + /// Move an array element out by replacing it with an /// [elision](ArrayExpressionElement::Elision). pub fn move_array_expression_element( diff --git a/crates/oxc_semantic/src/scope.rs b/crates/oxc_semantic/src/scope.rs index 91b0d599224f9d..9bf972bd82bb93 100644 --- a/crates/oxc_semantic/src/scope.rs +++ b/crates/oxc_semantic/src/scope.rs @@ -173,6 +173,16 @@ impl ScopeTree { } } + pub fn change_parent_id(&mut self, scope_id: ScopeId, new_parent_id: Option) { + let old_parent_id = self.parent_ids[scope_id]; + if self.build_child_ids { + if let Some(old_parent_id) = old_parent_id { + self.child_ids[old_parent_id].retain(|&child_id| child_id != scope_id); + } + } + self.set_parent_id(scope_id, new_parent_id); + } + /// Delete a scope. pub fn delete_scope(&mut self, scope_id: ScopeId) { if self.build_child_ids { @@ -311,6 +321,14 @@ impl ScopeTree { self.bindings[scope_id].shift_remove(name); } + /// Move a binding from one scope to another. + pub fn move_binding(&mut self, from: ScopeId, to: ScopeId, name: &str) { + let from_map = &mut self.bindings[from]; + if let Some((name, symbol_id)) = from_map.swap_remove_entry(name) { + self.bindings[to].insert(name, symbol_id); + } + } + /// Reserve memory for an `additional` number of scopes. pub fn reserve(&mut self, additional: usize) { self.parent_ids.reserve(additional); diff --git a/crates/oxc_transformer/src/common/statement_injector.rs b/crates/oxc_transformer/src/common/statement_injector.rs index 6a390b2b599010..d3387614e5f144 100644 --- a/crates/oxc_transformer/src/common/statement_injector.rs +++ b/crates/oxc_transformer/src/common/statement_injector.rs @@ -78,7 +78,6 @@ impl<'a> StatementInjectorStore<'a> { } /// Add a statement to be inserted immediately after the target statement. - #[expect(dead_code)] pub fn insert_after(&self, target: Address, stmt: Statement<'a>) { let mut insertions = self.insertions.borrow_mut(); let adjacent_stmts = insertions.entry(target).or_default(); @@ -131,145 +130,8 @@ impl<'a> StatementInjectorStore<'a> { new_statements.push(stmt); new_statements.extend(adjacent_stmts.into_iter().map(|s| s.stmt)); } - } - } - - *statements = new_statements; - } -} -//! Utility transform to add new statements before or after the specified statement. -//! -//! `StatementInjectorStore` contains a `FxHashMap>`. It is stored on `TransformCtx`. -//! -//! `StatementInjector` transform inserts new statements before or after a statement which is determined by the address of the statement. -//! -//! Other transforms can add statements to the store with following methods: -//! -//! ```rs -//! self.ctx.statement_injector.insert_before(address, statement); -//! self.ctx.statement_injector.insert_after(address, statement); -//! self.ctx.statement_injector.insert_many_after(address, statements); -//! ``` - -use std::cell::RefCell; - -use oxc_allocator::{Address, Vec as OxcVec}; - -use oxc_ast::{address::GetAddress, ast::*}; -use oxc_traverse::{Traverse, TraverseCtx}; -use rustc_hash::FxHashMap; - -use crate::TransformCtx; - -/// Transform that inserts any statements which have been requested insertion via `StatementInjectorStore` -pub struct StatementInjector<'a, 'ctx> { - ctx: &'ctx TransformCtx<'a>, -} - -impl<'a, 'ctx> StatementInjector<'a, 'ctx> { - pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self { - Self { ctx } - } -} - -impl<'a, 'ctx> Traverse<'a> for StatementInjector<'a, 'ctx> { - fn exit_statements( - &mut self, - statements: &mut OxcVec<'a, Statement<'a>>, - ctx: &mut TraverseCtx<'a>, - ) { - self.ctx.statement_injector.insert_into_statements(statements, ctx); - } -} - -enum Direction { - Before, - After, -} - -struct AdjacentStatement<'a> { - stmt: Statement<'a>, - direction: Direction, -} - -/// Store for statements to be added to the statements. -pub struct StatementInjectorStore<'a> { - insertions: RefCell>>>, -} - -// Public methods -impl<'a> StatementInjectorStore<'a> { - /// Create new `StatementInjectorStore`. - pub fn new() -> Self { - Self { insertions: RefCell::new(FxHashMap::default()) } - } - - /// Add a statement to be inserted immediately before the target statement. - #[expect(dead_code)] - pub fn insert_before(&self, target: Address, stmt: Statement<'a>) { - let mut insertions = self.insertions.borrow_mut(); - let adjacent_stmts = insertions.entry(target).or_default(); - let index = adjacent_stmts - .iter() - .position(|s| matches!(s.direction, Direction::After)) - .unwrap_or(adjacent_stmts.len()); - adjacent_stmts.insert(index, AdjacentStatement { stmt, direction: Direction::Before }); - } - - /// Add a statement to be inserted immediately after the target statement. - #[expect(dead_code)] - pub fn insert_after(&self, target: Address, stmt: Statement<'a>) { - let mut insertions = self.insertions.borrow_mut(); - let adjacent_stmts = insertions.entry(target).or_default(); - adjacent_stmts.push(AdjacentStatement { stmt, direction: Direction::After }); - } - - /// Add multiple statements to be inserted immediately after the target statement. - #[expect(dead_code)] - pub fn insert_many_after(&self, target: Address, stmts: Vec>) { - let mut insertions = self.insertions.borrow_mut(); - let adjacent_stmts = insertions.entry(target).or_default(); - adjacent_stmts.extend( - stmts.into_iter().map(|stmt| AdjacentStatement { stmt, direction: Direction::After }), - ); - } - - /// Insert statements immediately before / after the target statement. - pub(self) fn insert_into_statements( - &self, - statements: &mut OxcVec<'a, Statement<'a>>, - ctx: &mut TraverseCtx<'a>, - ) { - let mut insertions = self.insertions.borrow_mut(); - if insertions.is_empty() { - return; - } - - let new_statement_count = statements - .iter() - .filter_map(|s| insertions.get(&s.address()).map(Vec::len)) - .sum::(); - if new_statement_count == 0 { - return; - } - - let mut new_statements = ctx.ast.vec_with_capacity(statements.len() + new_statement_count); - - for stmt in statements.drain(..) { - if let Some(mut adjacent_stmts) = insertions.remove(&stmt.address()) { - let first_after_stmt_index = adjacent_stmts - .iter() - .position(|s| matches!(s.direction, Direction::After)) - .unwrap_or(adjacent_stmts.len()); - if first_after_stmt_index != 0 { - let right = adjacent_stmts.split_off(first_after_stmt_index); - new_statements.extend(adjacent_stmts.into_iter().map(|s| s.stmt)); - new_statements.push(stmt); - new_statements.extend(right.into_iter().map(|s| s.stmt)); - } else { - new_statements.push(stmt); - new_statements.extend(adjacent_stmts.into_iter().map(|s| s.stmt)); - } + } else { + new_statements.push(stmt); } } diff --git a/crates/oxc_transformer/src/es2017/async_to_generator.rs b/crates/oxc_transformer/src/es2017/async_to_generator.rs index 0e60db75e356ed..6eadfd3b84fabe 100644 --- a/crates/oxc_transformer/src/es2017/async_to_generator.rs +++ b/crates/oxc_transformer/src/es2017/async_to_generator.rs @@ -40,16 +40,12 @@ //! * Babel helper implementation: //! * Async / Await TC39 proposal: -use oxc_ast::{ - ast::{ - ArrowFunctionExpression, Expression, Function, FunctionType, Statement, - VariableDeclarationKind, - }, - NONE, -}; -use oxc_semantic::ScopeFlags; -use oxc_span::{GetSpan, SPAN}; -use oxc_traverse::{Ancestor, Traverse, TraverseCtx}; +use oxc_allocator::{Box, GetAddress}; +use oxc_ast::ast::*; +use oxc_ast::NONE; +use oxc_semantic::{ReferenceFlags, ScopeFlags, ScopeId, SymbolFlags}; +use oxc_span::{Atom, GetSpan, SPAN}; +use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx}; use crate::{common::helper_loader::Helper, TransformCtx}; @@ -96,11 +92,9 @@ impl<'a, 'ctx> Traverse<'a> for AsyncToGenerator<'a, 'ctx> { ); } } else if let Expression::FunctionExpression(func) = expr { - if !func.r#async || func.generator { - return; + if let Some(new_expr) = self.transform_function_expression(func, ctx) { + *expr = new_expr; } - let new_function = self.transform_function(func, ctx); - *expr = ctx.ast.expression_from_function(new_function); } else if let Expression::ArrowFunctionExpression(arrow) = expr { if !arrow.r#async { return; @@ -111,80 +105,182 @@ impl<'a, 'ctx> Traverse<'a> for AsyncToGenerator<'a, 'ctx> { fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { if let Statement::FunctionDeclaration(func) = stmt { - if !func.r#async || func.generator { - return; + if let Some(new_statement) = self.transform_function_declaration(func, ctx) { + self.ctx.statement_injector.insert_after(stmt.address(), new_statement); } - let new_function = self.transform_function(func, ctx); - if let Some(id) = func.id.take() { - *stmt = ctx.ast.statement_declaration(ctx.ast.declaration_variable( + } + } +} + +impl<'a, 'ctx> AsyncToGenerator<'a, 'ctx> { + fn transform_function_expression( + &self, + func: &mut Box<'a, Function<'a>>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + if !func.r#async || func.generator || func.is_typescript_syntax() { + return None; + } + + func.r#async = false; + let id = func.id.take(); + let body = func.body.take().unwrap(); + let params = ctx.ast.move_formal_parameters(&mut func.params); + let inner_function_scope_id = func.scope_id.take().unwrap(); + + // TODO: Handle `ignoreFunctionLength` + // + if id.is_none() && !params.has_parameter() { + return Some(self.generate_apply_helper_function( + params, + body, + inner_function_scope_id, + ctx, + )); + } + + let function_scope_id = + ctx.create_child_scope(ctx.current_scope_id(), ScopeFlags::Function); + // _ref + let ref_bound = ctx.generate_uid( + id.as_ref().map_or_else(|| "ref", |id| id.name.as_str()), + function_scope_id, + SymbolFlags::FunctionScopedVariable, + ); + + let apply_function = { + let scope_id = ctx.create_child_scope(function_scope_id, ScopeFlags::Function); + + // Move id to apply_function + if let Some(id) = id.as_ref() { + ctx.scopes_mut().move_binding( + inner_function_scope_id, + function_scope_id, + id.name.as_str(), + ); + let symbol_id = id.symbol_id.get().unwrap(); + let symbols = ctx.symbols_mut(); + *symbols.get_flags_mut(symbol_id) = SymbolFlags::FunctionScopedVariable; + symbols.set_scope_id(symbol_id, function_scope_id); + } + + let params = Self::generate_placeholder_parameters(¶ms, scope_id, ctx); + let statements = ctx.ast.vec1(Self::generate_apply_call_statement(&ref_bound, ctx)); + let body = ctx.ast.function_body(SPAN, ctx.ast.vec(), statements); + Self::create_function(id, params, body, scope_id, ctx) + }; + + { + debug_assert!(func.body.is_none()); + debug_assert!(func.scope_id.get().is_none()); + + let mut statements = ctx.ast.vec_with_capacity(3); + let expression = + self.generate_apply_helper_function(params, body, inner_function_scope_id, ctx); + // TODO: Make it more clear + ctx.scopes_mut().change_parent_id(inner_function_scope_id, Some(function_scope_id)); + + statements.push(Self::get_helper_call_declaration(&ref_bound, expression, ctx)); + + if let Some(id) = apply_function.id.as_ref() { + let reference = ctx.create_bound_reference_id( SPAN, - VariableDeclarationKind::Const, - ctx.ast.vec1(ctx.ast.variable_declarator( - SPAN, - VariableDeclarationKind::Const, - ctx.ast.binding_pattern( - ctx.ast.binding_pattern_kind_from_binding_identifier(id), - NONE, - false, - ), - Some(ctx.ast.expression_from_function(new_function)), - false, - )), - false, - )); + id.name.clone(), + id.symbol_id.get().unwrap(), + ReferenceFlags::Read, + ); + let statement = Statement::from(ctx.ast.declaration_from_function(apply_function)); + statements.push(statement); + let argument = Some(ctx.ast.expression_from_identifier_reference(reference)); + statements.push(ctx.ast.statement_return(SPAN, argument)); } else { - *stmt = - ctx.ast.statement_declaration(ctx.ast.declaration_from_function(new_function)); + let statement_return = ctx + .ast + .statement_return(SPAN, Some(ctx.ast.expression_from_function(apply_function))); + statements.push(statement_return); } + + func.body.replace(ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), statements)); + func.scope_id.set(Some(function_scope_id)); } + + // Self reference call + let callee = ctx.ast.expression_from_function(ctx.ast.move_function(func)); + Some(ctx.ast.expression_call(SPAN, callee, NONE, ctx.ast.vec(), false)) } -} -impl<'a, 'ctx> AsyncToGenerator<'a, 'ctx> { - fn transform_function( + /// ```js + /// function NAME(PARAMS) { return REF.apply(this, arguments); } + /// function REF() { + /// REF = FUNCTION; + /// return REF.apply(this, arguments); + /// } + /// ``` + fn transform_function_declaration( &self, - func: &mut Function<'a>, + func: &mut Box<'a, Function<'a>>, ctx: &mut TraverseCtx<'a>, - ) -> Function<'a> { - let target = ctx.ast.function( - func.r#type, - SPAN, - None, - true, - false, - false, - func.type_parameters.take(), - func.this_param.take(), - ctx.ast.alloc(ctx.ast.formal_parameters( - SPAN, - func.params.kind, - ctx.ast.move_vec(&mut func.params.items), - func.params.rest.take(), - )), - func.return_type.take(), - func.body.take(), + ) -> Option> { + if !func.r#async || func.generator || func.is_typescript_syntax() { + return None; + } + + func.r#async = false; + let body = func.body.take().unwrap(); + let params = ctx.ast.move_formal_parameters(&mut func.params); + let apply_function_scope_id = func.scope_id.take().unwrap(); + let function_scope_id = + ctx.create_child_scope(ctx.current_scope_id(), ScopeFlags::Function); + + // _ref + let ref_bound = ctx.generate_uid_in_current_scope( + func.id.as_ref().map_or_else(|| "ref", |id| id.name.as_str()), + SymbolFlags::FunctionScopedVariable, ); - let parameters = - ctx.ast.vec1(ctx.ast.argument_expression(ctx.ast.expression_from_function(target))); - let call = self.ctx.helper_call_expr(Helper::AsyncToGenerator, parameters, ctx); - let returns = ctx.ast.return_statement(SPAN, Some(call)); - let body = Statement::ReturnStatement(ctx.ast.alloc(returns)); - let body = ctx.ast.function_body(SPAN, ctx.ast.vec(), ctx.ast.vec1(body)); - let body = ctx.ast.alloc(body); - let params = ctx.ast.formal_parameters(SPAN, func.params.kind, ctx.ast.vec(), NONE); - ctx.ast.function( - FunctionType::FunctionExpression, - SPAN, - None, - false, - false, - false, - func.type_parameters.take(), - func.this_param.take(), - params, - func.return_type.take(), - Some(body), - ) + + let mut statements = ctx.ast.vec_with_capacity(2); + let expression = + self.generate_apply_helper_function(params, body, apply_function_scope_id, ctx); + statements.push(Self::get_helper_call_assignment( + ref_bound.create_write_reference(ctx), + expression, + ctx, + )); + statements.push(Self::generate_apply_call_statement(&ref_bound, ctx)); + + // function () { _ref.apply(this, arguments); } + let apply_function = { + let scope_id = ctx.create_child_scope(ctx.current_scope_id(), ScopeFlags::Function); + + // Exchange the parent scope of the function scope with the current scope. + // TODO: Make it more clear + ctx.scopes_mut().change_parent_id(apply_function_scope_id, Some(scope_id)); + + let params = Self::create_empty_params(ctx); + let body = ctx.ast.function_body(SPAN, ctx.ast.vec(), statements); + Self::create_function( + Some(ref_bound.create_binding_identifier()), + params, + body, + scope_id, + ctx, + ) + }; + + // Modify the wrapper function + { + debug_assert!(func.body.is_none()); + func.params = Self::generate_placeholder_parameters( + &apply_function.params, + function_scope_id, + ctx, + ); + let statements = ctx.ast.vec1(Self::generate_apply_call_statement(&ref_bound, ctx)); + func.body.replace(ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), statements)); + func.scope_id.set(Some(function_scope_id)); + } + + Some(Statement::from(ctx.ast.declaration_from_function(apply_function))) } fn transform_arrow_function( @@ -192,11 +288,7 @@ impl<'a, 'ctx> AsyncToGenerator<'a, 'ctx> { arrow: &mut ArrowFunctionExpression<'a>, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { - let mut body = ctx.ast.function_body( - SPAN, - ctx.ast.move_vec(&mut arrow.body.directives), - ctx.ast.move_vec(&mut arrow.body.statements), - ); + let mut body = ctx.ast.move_function_body(&mut arrow.body); // If the arrow's expression is true, we need to wrap the only one expression with return statement. if arrow.expression { @@ -208,24 +300,191 @@ impl<'a, 'ctx> AsyncToGenerator<'a, 'ctx> { *statement = ctx.ast.statement_return(expression.span(), Some(expression)); } - let r#type = FunctionType::FunctionExpression; - let parameters = ctx.ast.alloc(ctx.ast.formal_parameters( + let scope_id = arrow.scope_id.get().unwrap(); + ctx.scopes_mut().get_flags_mut(scope_id).remove(ScopeFlags::Arrow); + + self.generate_apply_helper_function( + ctx.ast.move_formal_parameters(&mut arrow.params), + ctx.ast.alloc(body), + scope_id, + ctx, + ) + } + + /// Passing specified parameters to create a function. + fn create_function( + id: Option>, + params: Box<'a, FormalParameters<'a>>, + body: FunctionBody<'a>, + scope_id: ScopeId, + ctx: &mut TraverseCtx<'a>, + ) -> Function<'a> { + let r#type = if id.is_some() { + FunctionType::FunctionDeclaration + } else { + FunctionType::FunctionExpression + }; + ctx.ast.function_with_scope_id( + r#type, + SPAN, + id, + false, + false, + false, + NONE, + NONE, + params, + NONE, + Some(body), + scope_id, + ) + } + + /// Call `apply` method for the passed-in reference, with `this` and `arguments` as arguments. + /// + /// ```js + /// ref_bound.apply(this, arguments); + /// ``` + fn generate_apply_call_statement( + ref_bound: &BoundIdentifier<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Statement<'a> { + let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), "arguments"); + let arguments_ident = + ctx.create_reference_id(SPAN, Atom::from("arguments"), symbol_id, ReferenceFlags::Read); + let arguments_ident = ctx.ast.expression_from_identifier_reference(arguments_ident); + + // (this, arguments) + let mut arguments = ctx.ast.vec_with_capacity(2); + arguments.push(ctx.ast.argument_expression(ctx.ast.expression_this(SPAN))); + arguments.push(ctx.ast.argument_expression(arguments_ident)); + // _ref.apply + let callee = ctx.ast.expression_member(ctx.ast.member_expression_static( SPAN, - arrow.params.kind, - ctx.ast.move_vec(&mut arrow.params.items), - arrow.params.rest.take(), + ref_bound.create_read_expression(ctx), + ctx.ast.identifier_name(SPAN, "apply"), + false, )); - let body = Some(body); - let mut function = ctx - .ast - .function(r#type, SPAN, None, true, false, false, NONE, NONE, parameters, NONE, body); - function.scope_id = arrow.scope_id.clone(); - if let Some(scope_id) = function.scope_id.get() { - ctx.scopes_mut().get_flags_mut(scope_id).remove(ScopeFlags::Arrow); - } + let argument = ctx.ast.expression_call(SPAN, callee, NONE, arguments, false); + ctx.ast.statement_return(SPAN, Some(argument)) + } + /// Returns an [`Expression`] that calls [`Helper::AsyncToGenerator`] helper function with arguments + /// constructed from the passed-in parameters, body, and scope_id. + /// + /// The generated code looks like: + /// ```js + /// asyncToGenerator(function* (PARAMS) { + /// BODY + /// }); + /// ``` + fn generate_apply_helper_function( + &self, + parameters: FormalParameters<'a>, + body: Box<'a, FunctionBody<'a>>, + scope_id: ScopeId, + ctx: &mut TraverseCtx<'a>, + ) -> Expression<'a> { + let r#type = FunctionType::FunctionExpression; + let function = ctx.ast.function_with_scope_id( + r#type, + SPAN, + None, + true, + false, + false, + NONE, + NONE, + parameters, + NONE, + Some(body), + scope_id, + ); let arguments = ctx.ast.vec1(ctx.ast.argument_expression(ctx.ast.expression_from_function(function))); self.ctx.helper_call_expr(Helper::AsyncToGenerator, arguments, ctx) } + + /// Generate a [`VariableDeclaration`] for the ref and pass the expression as init of [`VariableDeclarator`] + /// + /// The generated code looks like: + /// ```js + /// var ref = EXPRESSION; + /// ``` + fn get_helper_call_declaration( + ref_bound: &BoundIdentifier<'a>, + expression: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Statement<'a> { + let declarations = ctx.ast.vec1(ctx.ast.variable_declarator( + SPAN, + VariableDeclarationKind::Var, + ref_bound.create_binding_pattern(ctx), + Some(expression), + false, + )); + ctx.ast.statement_declaration(ctx.ast.declaration_variable( + SPAN, + VariableDeclarationKind::Var, + declarations, + false, + )) + } + + /// Generate an [`AssignmentExpression`] for the ident and pass the expression as the right side of the assignment. + /// + /// The generated code looks like: + /// ```js + /// ident = EXPRESSION; + /// ``` + fn get_helper_call_assignment( + ident: IdentifierReference<'a>, + expression: Expression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Statement<'a> { + let expression = ctx.ast.expression_assignment( + SPAN, + AssignmentOperator::Assign, + AssignmentTarget::from( + ctx.ast.simple_assignment_target_from_identifier_reference(ident), + ), + expression, + ); + ctx.ast.statement_expression(SPAN, expression) + } + + /// Generate placeholder [`FormalParameters`] which named `_x` based on the passed-in parameters. + /// `function p(x, y, z, d = 0, ...rest) {}` -> `function* (_x, _x1, _x2) {}` + fn generate_placeholder_parameters( + params: &FormalParameters<'a>, + scope_id: ScopeId, + ctx: &mut TraverseCtx<'a>, + ) -> Box<'a, FormalParameters<'a>> { + let mut parameters = ctx.ast.vec_with_capacity(params.items.len()); + for param in ¶ms.items { + let binding = ctx.generate_uid("x", scope_id, SymbolFlags::FunctionScopedVariable); + parameters.push( + ctx.ast.plain_formal_parameter(param.span(), binding.create_binding_pattern(ctx)), + ); + } + let parameters = ctx.ast.alloc_formal_parameters( + SPAN, + FormalParameterKind::FormalParameter, + parameters, + NONE, + ); + + parameters + } + + /// Create an empty [FormalParameters] with [FormalParameterKind::FormalParameter]. + fn create_empty_params(ctx: &mut TraverseCtx<'a>) -> Box<'a, FormalParameters<'a>> { + let apply_function_params = ctx.ast.alloc_formal_parameters( + SPAN, + FormalParameterKind::FormalParameter, + ctx.ast.vec(), + NONE, + ); + apply_function_params + } } diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 2a60003e24a737..90a2efd431b474 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -241,7 +241,6 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> { fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) { self.x0_typescript.exit_function(func, ctx); self.x1_react.exit_function(func, ctx); - self.x2_es2017.exit_function(func, ctx); self.x3_es2015.exit_function(func, ctx); } diff --git a/tasks/transform_conformance/snapshots/babel.snap.md b/tasks/transform_conformance/snapshots/babel.snap.md index 4faa31262c0e60..c8eeb561833cd8 100644 --- a/tasks/transform_conformance/snapshots/babel.snap.md +++ b/tasks/transform_conformance/snapshots/babel.snap.md @@ -1,6 +1,6 @@ commit: d20b314c -Passed: 348/1058 +Passed: 353/1058 # All Passed: * babel-plugin-transform-class-static-block @@ -1632,7 +1632,7 @@ x Output mismatch x Output mismatch -# babel-plugin-transform-async-to-generator (2/24) +# babel-plugin-transform-async-to-generator (7/24) * assumption-ignoreFunctionLength-true/basic/input.mjs x Output mismatch @@ -1654,15 +1654,6 @@ x Output mismatch * async-to-generator/object-method-with-super/input.js x Output mismatch -* async-to-generator/shadowed-promise/input.js -x Output mismatch - -* async-to-generator/shadowed-promise-import/input.mjs -x Output mismatch - -* async-to-generator/shadowed-promise-nested/input.js -x Output mismatch - * bluebird-coroutines/arrow-function/input.js x Output mismatch @@ -1681,15 +1672,9 @@ x Output mismatch * regression/15978/input.js x Output mismatch -* regression/8783/input.js -x Output mismatch - * regression/T7108/input.js x Output mismatch -* regression/T7194/input.js -x Output mismatch - * regression/gh-6923/input.js x Output mismatch