mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
refactor(transformer): add TransformerCtx struct for easier access to symbols and scopes
This commit is contained in:
parent
94792e9153
commit
46a5c42c75
10 changed files with 71 additions and 43 deletions
|
|
@ -34,13 +34,14 @@ fn main() {
|
|||
println!("{printed}");
|
||||
|
||||
let semantic = SemanticBuilder::new(&source_text, source_type).build(&ret.program).semantic;
|
||||
let (symbols, _scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
||||
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
|
||||
let symbols = Rc::new(RefCell::new(symbols));
|
||||
let scopes = Rc::new(RefCell::new(scopes));
|
||||
|
||||
let program = allocator.alloc(ret.program);
|
||||
let transform_options =
|
||||
TransformOptions { target: TransformTarget::ES2015, ..TransformOptions::default() };
|
||||
Transformer::new(&allocator, source_type, &symbols, transform_options).build(program);
|
||||
Transformer::new(&allocator, source_type, &symbols, &scopes, transform_options).build(program);
|
||||
let printed = Codegen::<false>::new(source_text.len(), codegen_options).build(program);
|
||||
println!("Transformed:\n");
|
||||
println!("{printed}");
|
||||
|
|
|
|||
11
crates/oxc_transformer/src/context.rs
Normal file
11
crates/oxc_transformer/src/context.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use oxc_ast::AstBuilder;
|
||||
use oxc_semantic::{ScopeTree, SymbolTable};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TransformerCtx<'a> {
|
||||
pub ast: Rc<AstBuilder<'a>>,
|
||||
pub symbols: Rc<RefCell<SymbolTable>>,
|
||||
pub scopes: Rc<RefCell<ScopeTree>>,
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::rc::Rc;
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_semantic::SymbolTable;
|
||||
use oxc_span::{Atom, Span};
|
||||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
|
||||
|
||||
use crate::{
|
||||
context::TransformerCtx,
|
||||
options::{TransformOptions, TransformTarget},
|
||||
utils::CreateVars,
|
||||
};
|
||||
|
|
@ -19,7 +19,7 @@ use crate::{
|
|||
/// * <https://github.com/babel/babel/blob/main/packages/babel-helper-builder-binary-assignment-operator-visitor>
|
||||
pub struct ExponentiationOperator<'a> {
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
symbols: Rc<RefCell<SymbolTable>>,
|
||||
ctx: TransformerCtx<'a>,
|
||||
vars: Vec<'a, VariableDeclarator<'a>>,
|
||||
}
|
||||
|
||||
|
|
@ -29,8 +29,8 @@ struct Exploded<'a> {
|
|||
}
|
||||
|
||||
impl<'a> CreateVars<'a> for ExponentiationOperator<'a> {
|
||||
fn ast(&self) -> &AstBuilder<'a> {
|
||||
&self.ast
|
||||
fn ctx(&self) -> &TransformerCtx<'a> {
|
||||
&self.ctx
|
||||
}
|
||||
|
||||
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> {
|
||||
|
|
@ -41,12 +41,12 @@ impl<'a> CreateVars<'a> for ExponentiationOperator<'a> {
|
|||
impl<'a> ExponentiationOperator<'a> {
|
||||
pub fn new(
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
symbols: Rc<RefCell<SymbolTable>>,
|
||||
ctx: TransformerCtx<'a>,
|
||||
options: &TransformOptions,
|
||||
) -> Option<Self> {
|
||||
(options.target < TransformTarget::ES2016 || options.exponentiation_operator).then(|| {
|
||||
let vars = ast.new_vec();
|
||||
Self { ast, symbols, vars }
|
||||
Self { ast, ctx, vars }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ impl<'a> ExponentiationOperator<'a> {
|
|||
if ident
|
||||
.reference_id
|
||||
.get()
|
||||
.is_some_and(|reference_id| self.symbols.borrow().has_binding(reference_id))
|
||||
.is_some_and(|reference_id| self.ctx.symbols.borrow().has_binding(reference_id))
|
||||
{
|
||||
// this variable is declared in scope so we can be 100% sure
|
||||
// that evaluating it multiple times won't trigger a getter
|
||||
|
|
@ -185,7 +185,7 @@ impl<'a> ExponentiationOperator<'a> {
|
|||
// Super cannot be directly assigned so lets return it also
|
||||
if matches!(expr, Expression::Super(_))
|
||||
|| matches!(&expr, Expression::Identifier(ident) if
|
||||
ident.reference_id.get().is_some_and(|reference_id| self.symbols.borrow().has_binding(reference_id)))
|
||||
ident.reference_id.get().is_some_and(|reference_id| self.ctx.symbols.borrow().has_binding(reference_id)))
|
||||
{
|
||||
return Some(expr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
use serde::Deserialize;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::rc::Rc;
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_semantic::SymbolTable;
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator};
|
||||
|
||||
use crate::{utils::CreateVars, TransformOptions, TransformTarget};
|
||||
use crate::{
|
||||
context::TransformerCtx,
|
||||
options::{TransformOptions, TransformTarget},
|
||||
utils::CreateVars,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
||||
pub struct NullishCoalescingOperatorOptions {
|
||||
|
|
@ -26,13 +29,13 @@ pub struct NullishCoalescingOperator<'a> {
|
|||
no_document_all: bool,
|
||||
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
symbols: Rc<RefCell<SymbolTable>>,
|
||||
ctx: TransformerCtx<'a>,
|
||||
vars: Vec<'a, VariableDeclarator<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> CreateVars<'a> for NullishCoalescingOperator<'a> {
|
||||
fn ast(&self) -> &AstBuilder<'a> {
|
||||
&self.ast
|
||||
fn ctx(&self) -> &TransformerCtx<'a> {
|
||||
&self.ctx
|
||||
}
|
||||
|
||||
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> {
|
||||
|
|
@ -43,7 +46,7 @@ impl<'a> CreateVars<'a> for NullishCoalescingOperator<'a> {
|
|||
impl<'a> NullishCoalescingOperator<'a> {
|
||||
pub fn new(
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
symbols: Rc<RefCell<SymbolTable>>,
|
||||
ctx: TransformerCtx<'a>,
|
||||
options: &TransformOptions,
|
||||
) -> Option<Self> {
|
||||
(options.target < TransformTarget::ES2020 || options.nullish_coalescing_operator.is_some())
|
||||
|
|
@ -51,7 +54,7 @@ impl<'a> NullishCoalescingOperator<'a> {
|
|||
let no_document_all = options.assumptions.no_document_all
|
||||
|| options.nullish_coalescing_operator.is_some_and(|o| o.loose);
|
||||
let vars = ast.new_vec();
|
||||
Self { no_document_all, ast, symbols, vars }
|
||||
Self { no_document_all, ast, ctx, vars }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +70,7 @@ impl<'a> NullishCoalescingOperator<'a> {
|
|||
let assignment;
|
||||
|
||||
// skip creating extra reference when `left` is static
|
||||
if self.symbols.borrow().is_static(&logical_expr.left) {
|
||||
if self.ctx.symbols.borrow().is_static(&logical_expr.left) {
|
||||
reference = self.ast.copy(&logical_expr.left);
|
||||
assignment = self.ast.copy(&logical_expr.left);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
//! * <https://babel.dev/docs/presets>
|
||||
//! * <https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts>
|
||||
|
||||
mod context;
|
||||
mod es2015;
|
||||
mod es2016;
|
||||
mod es2019;
|
||||
|
|
@ -25,14 +26,14 @@ use std::{cell::RefCell, rc::Rc};
|
|||
|
||||
use oxc_allocator::{Allocator, Vec};
|
||||
use oxc_ast::{ast::*, AstBuilder, VisitMut};
|
||||
use oxc_semantic::SymbolTable;
|
||||
use oxc_semantic::{ScopeTree, SymbolTable};
|
||||
use oxc_span::SourceType;
|
||||
|
||||
use crate::{
|
||||
es2015::ShorthandProperties, es2016::ExponentiationOperator, es2019::OptionalCatchBinding,
|
||||
es2020::NullishCoalescingOperator, es2021::LogicalAssignmentOperators,
|
||||
es2022::ClassStaticBlock, react_jsx::ReactJsx, regexp::RegexpFlags, typescript::TypeScript,
|
||||
utils::CreateVars,
|
||||
context::TransformerCtx, es2015::ShorthandProperties, es2016::ExponentiationOperator,
|
||||
es2019::OptionalCatchBinding, es2020::NullishCoalescingOperator,
|
||||
es2021::LogicalAssignmentOperators, es2022::ClassStaticBlock, react_jsx::ReactJsx,
|
||||
regexp::RegexpFlags, typescript::TypeScript, utils::CreateVars,
|
||||
};
|
||||
|
||||
pub use crate::{
|
||||
|
|
@ -41,7 +42,6 @@ pub use crate::{
|
|||
react_jsx::{ReactJsxOptions, ReactJsxRuntime},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Transformer<'a> {
|
||||
#[allow(unused)]
|
||||
typescript: Option<TypeScript<'a>>,
|
||||
|
|
@ -68,18 +68,24 @@ impl<'a> Transformer<'a> {
|
|||
allocator: &'a Allocator,
|
||||
source_type: SourceType,
|
||||
symbols: &Rc<RefCell<SymbolTable>>,
|
||||
scopes: &Rc<RefCell<ScopeTree>>,
|
||||
options: TransformOptions,
|
||||
) -> Self {
|
||||
let ast = Rc::new(AstBuilder::new(allocator));
|
||||
let ctx = TransformerCtx {
|
||||
ast: Rc::clone(&ast),
|
||||
symbols: Rc::clone(symbols),
|
||||
scopes: Rc::clone(scopes),
|
||||
};
|
||||
Self {
|
||||
typescript: source_type.is_typescript().then(|| TypeScript::new(Rc::clone(&ast))),
|
||||
react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), options)),
|
||||
regexp_flags: RegexpFlags::new(Rc::clone(&ast), &options),
|
||||
es2022_class_static_block: es2022::ClassStaticBlock::new(Rc::clone(&ast), &options),
|
||||
es2021_logical_assignment_operators: LogicalAssignmentOperators::new(Rc::clone(&ast), &options),
|
||||
es2020_nullish_coalescing_operators: NullishCoalescingOperator::new(Rc::clone(&ast), Rc::clone(symbols), &options),
|
||||
es2020_nullish_coalescing_operators: NullishCoalescingOperator::new(Rc::clone(&ast), ctx.clone(), &options),
|
||||
es2019_optional_catch_binding: OptionalCatchBinding::new(Rc::clone(&ast), &options),
|
||||
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), Rc::clone(symbols), &options),
|
||||
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options),
|
||||
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,11 +34,13 @@ impl Tester {
|
|||
let program = Parser::new(&self.allocator, source_text, self.source_type).parse().program;
|
||||
|
||||
let semantic = SemanticBuilder::new(source_text, self.source_type).build(&program).semantic;
|
||||
let (symbols, _scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
||||
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
|
||||
let symbols = Rc::new(RefCell::new(symbols));
|
||||
let scopes = Rc::new(RefCell::new(scopes));
|
||||
|
||||
let program = self.allocator.alloc(program);
|
||||
Transformer::new(&self.allocator, self.source_type, &symbols, self.options).build(program);
|
||||
Transformer::new(&self.allocator, self.source_type, &symbols, &scopes, self.options)
|
||||
.build(program);
|
||||
Codegen::<false>::new(source_text.len(), CodegenOptions).build(program)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::mem;
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{Atom, Span};
|
||||
|
||||
use crate::context::TransformerCtx;
|
||||
|
||||
// TODO:
|
||||
// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L543>
|
||||
pub fn generate_uid_based_on_node(expr: &Expression) -> Atom {
|
||||
|
|
@ -52,7 +54,7 @@ impl GatherNodeParts for PrivateIdentifier {
|
|||
}
|
||||
|
||||
pub trait CreateVars<'a> {
|
||||
fn ast(&self) -> &AstBuilder<'a>;
|
||||
fn ctx(&self) -> &TransformerCtx<'a>;
|
||||
|
||||
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>>;
|
||||
|
||||
|
|
@ -60,11 +62,11 @@ pub trait CreateVars<'a> {
|
|||
if self.vars_mut().is_empty() {
|
||||
return;
|
||||
}
|
||||
let new_vec = self.ast().new_vec();
|
||||
let new_vec = self.ctx().ast.new_vec();
|
||||
let decls = mem::replace(self.vars_mut(), new_vec);
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let decl =
|
||||
self.ast().variable_declaration(Span::default(), kind, decls, Modifiers::empty());
|
||||
self.ctx().ast.variable_declaration(Span::default(), kind, decls, Modifiers::empty());
|
||||
let stmt = Statement::Declaration(Declaration::VariableDeclaration(decl));
|
||||
stmts.insert(0, stmt);
|
||||
}
|
||||
|
|
@ -75,10 +77,10 @@ pub trait CreateVars<'a> {
|
|||
|
||||
// Add `var name` to scope
|
||||
let binding_identifier = BindingIdentifier::new(Span::default(), name.clone());
|
||||
let binding_pattern_kind = self.ast().binding_pattern_identifier(binding_identifier);
|
||||
let binding = self.ast().binding_pattern(binding_pattern_kind, None, false);
|
||||
let binding_pattern_kind = self.ctx().ast.binding_pattern_identifier(binding_identifier);
|
||||
let binding = self.ctx().ast.binding_pattern(binding_pattern_kind, None, false);
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let decl = self.ast().variable_declarator(Span::default(), kind, binding, None, false);
|
||||
let decl = self.ctx().ast.variable_declarator(Span::default(), kind, binding, None, false);
|
||||
self.vars_mut().push(decl);
|
||||
name
|
||||
}
|
||||
|
|
|
|||
|
|
@ -213,11 +213,12 @@ impl Oxc {
|
|||
// FIXME: this should not be duplicated with the linter semantic,
|
||||
// we need to fix the API so symbols and scopes can be shared.
|
||||
let semantic = SemanticBuilder::new(source_text, source_type).build(program).semantic;
|
||||
let (symbols, _scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
||||
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
|
||||
let symbols = Rc::new(RefCell::new(symbols));
|
||||
let scopes = Rc::new(RefCell::new(scopes));
|
||||
let options =
|
||||
TransformOptions { target: TransformTarget::ES2015, ..TransformOptions::default() };
|
||||
Transformer::new(&allocator, source_type, &symbols, options).build(program);
|
||||
Transformer::new(&allocator, source_type, &symbols, &scopes, options).build(program);
|
||||
}
|
||||
|
||||
let program = allocator.alloc(program);
|
||||
|
|
|
|||
|
|
@ -35,11 +35,12 @@ fn bench_transformer(criterion: &mut Criterion) {
|
|||
let program = Parser::new(&allocator, source_text, source_type).parse().program;
|
||||
let semantic =
|
||||
SemanticBuilder::new(source_text, source_type).build(&program).semantic;
|
||||
let (symbols, _scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
||||
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
|
||||
let symbols = Rc::new(RefCell::new(symbols));
|
||||
let scopes = Rc::new(RefCell::new(scopes));
|
||||
let program = allocator.alloc(program);
|
||||
let transform_options = TransformOptions::default();
|
||||
Transformer::new(&allocator, source_type, &symbols, transform_options)
|
||||
Transformer::new(&allocator, source_type, &symbols, &scopes, transform_options)
|
||||
.build(black_box(program));
|
||||
allocator
|
||||
});
|
||||
|
|
|
|||
|
|
@ -213,10 +213,11 @@ impl TestCase {
|
|||
// Transform input.js
|
||||
let program = Parser::new(&allocator, &input, source_type).parse().program;
|
||||
let semantic = SemanticBuilder::new(&input, source_type).build(&program).semantic;
|
||||
let (symbols, _scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
||||
let (symbols, scopes) = semantic.into_symbol_table_and_scope_tree();
|
||||
let symbols = Rc::new(RefCell::new(symbols));
|
||||
let scopes = Rc::new(RefCell::new(scopes));
|
||||
let program = allocator.alloc(program);
|
||||
Transformer::new(&allocator, source_type, &symbols, self.transform_options())
|
||||
Transformer::new(&allocator, source_type, &symbols, &scopes, self.transform_options())
|
||||
.build(program);
|
||||
let transformed_code = Codegen::<false>::new(input.len(), CodegenOptions).build(program);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue