Skip to content

Commit

Permalink
feat(transformer/async-to-generator): transform functions and arrow f…
Browse files Browse the repository at this point in the history
…unctions which have no id and no arguments
  • Loading branch information
Dunqing committed Oct 22, 2024
1 parent 803eff6 commit 3e43a15
Show file tree
Hide file tree
Showing 6 changed files with 416 additions and 254 deletions.
39 changes: 39 additions & 0 deletions crates/oxc_ast/src/ast_builder_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
18 changes: 18 additions & 0 deletions crates/oxc_semantic/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ impl ScopeTree {
}
}

pub fn change_parent_id(&mut self, scope_id: ScopeId, new_parent_id: Option<ScopeId>) {
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 {
Expand Down Expand Up @@ -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);
Expand Down
142 changes: 2 additions & 140 deletions crates/oxc_transformer/src/common/statement_injector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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<Address, Vec<AdjacentStatement>>`. 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<FxHashMap<Address, Vec<AdjacentStatement<'a>>>>,
}

// 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<Statement<'a>>) {
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::<usize>();
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);
}
}

Expand Down
Loading

0 comments on commit 3e43a15

Please sign in to comment.