refactor(transformer): add TransformerCtx struct for easier access to symbols and scopes

This commit is contained in:
Boshen 2023-10-19 16:09:53 +08:00
parent 94792e9153
commit 46a5c42c75
No known key found for this signature in database
GPG key ID: 234DA6A7079C6801
10 changed files with 71 additions and 43 deletions

View file

@ -34,13 +34,14 @@ fn main() {
println!("{printed}"); println!("{printed}");
let semantic = SemanticBuilder::new(&source_text, source_type).build(&ret.program).semantic; 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 symbols = Rc::new(RefCell::new(symbols));
let scopes = Rc::new(RefCell::new(scopes));
let program = allocator.alloc(ret.program); let program = allocator.alloc(ret.program);
let transform_options = let transform_options =
TransformOptions { target: TransformTarget::ES2015, ..TransformOptions::default() }; 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); let printed = Codegen::<false>::new(source_text.len(), codegen_options).build(program);
println!("Transformed:\n"); println!("Transformed:\n");
println!("{printed}"); println!("{printed}");

View 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>>,
}

View file

@ -1,12 +1,12 @@
use std::{cell::RefCell, rc::Rc}; use std::rc::Rc;
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::{ast::*, AstBuilder};
use oxc_semantic::SymbolTable;
use oxc_span::{Atom, Span}; use oxc_span::{Atom, Span};
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator}; use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
use crate::{ use crate::{
context::TransformerCtx,
options::{TransformOptions, TransformTarget}, options::{TransformOptions, TransformTarget},
utils::CreateVars, utils::CreateVars,
}; };
@ -19,7 +19,7 @@ use crate::{
/// * <https://github.com/babel/babel/blob/main/packages/babel-helper-builder-binary-assignment-operator-visitor> /// * <https://github.com/babel/babel/blob/main/packages/babel-helper-builder-binary-assignment-operator-visitor>
pub struct ExponentiationOperator<'a> { pub struct ExponentiationOperator<'a> {
ast: Rc<AstBuilder<'a>>, ast: Rc<AstBuilder<'a>>,
symbols: Rc<RefCell<SymbolTable>>, ctx: TransformerCtx<'a>,
vars: Vec<'a, VariableDeclarator<'a>>, vars: Vec<'a, VariableDeclarator<'a>>,
} }
@ -29,8 +29,8 @@ struct Exploded<'a> {
} }
impl<'a> CreateVars<'a> for ExponentiationOperator<'a> { impl<'a> CreateVars<'a> for ExponentiationOperator<'a> {
fn ast(&self) -> &AstBuilder<'a> { fn ctx(&self) -> &TransformerCtx<'a> {
&self.ast &self.ctx
} }
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> { 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> { impl<'a> ExponentiationOperator<'a> {
pub fn new( pub fn new(
ast: Rc<AstBuilder<'a>>, ast: Rc<AstBuilder<'a>>,
symbols: Rc<RefCell<SymbolTable>>, ctx: TransformerCtx<'a>,
options: &TransformOptions, options: &TransformOptions,
) -> Option<Self> { ) -> Option<Self> {
(options.target < TransformTarget::ES2016 || options.exponentiation_operator).then(|| { (options.target < TransformTarget::ES2016 || options.exponentiation_operator).then(|| {
let vars = ast.new_vec(); let vars = ast.new_vec();
Self { ast, symbols, vars } Self { ast, ctx, vars }
}) })
} }
@ -163,7 +163,7 @@ impl<'a> ExponentiationOperator<'a> {
if ident if ident
.reference_id .reference_id
.get() .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 // this variable is declared in scope so we can be 100% sure
// that evaluating it multiple times won't trigger a getter // 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 // Super cannot be directly assigned so lets return it also
if matches!(expr, Expression::Super(_)) if matches!(expr, Expression::Super(_))
|| matches!(&expr, Expression::Identifier(ident) if || 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); return Some(expr);
} }

View file

@ -1,13 +1,16 @@
use serde::Deserialize; use serde::Deserialize;
use std::{cell::RefCell, rc::Rc}; use std::rc::Rc;
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::{ast::*, AstBuilder};
use oxc_semantic::SymbolTable;
use oxc_span::Span; use oxc_span::Span;
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator}; 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)] #[derive(Debug, Default, Clone, Copy, Deserialize)]
pub struct NullishCoalescingOperatorOptions { pub struct NullishCoalescingOperatorOptions {
@ -26,13 +29,13 @@ pub struct NullishCoalescingOperator<'a> {
no_document_all: bool, no_document_all: bool,
ast: Rc<AstBuilder<'a>>, ast: Rc<AstBuilder<'a>>,
symbols: Rc<RefCell<SymbolTable>>, ctx: TransformerCtx<'a>,
vars: Vec<'a, VariableDeclarator<'a>>, vars: Vec<'a, VariableDeclarator<'a>>,
} }
impl<'a> CreateVars<'a> for NullishCoalescingOperator<'a> { impl<'a> CreateVars<'a> for NullishCoalescingOperator<'a> {
fn ast(&self) -> &AstBuilder<'a> { fn ctx(&self) -> &TransformerCtx<'a> {
&self.ast &self.ctx
} }
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> { 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> { impl<'a> NullishCoalescingOperator<'a> {
pub fn new( pub fn new(
ast: Rc<AstBuilder<'a>>, ast: Rc<AstBuilder<'a>>,
symbols: Rc<RefCell<SymbolTable>>, ctx: TransformerCtx<'a>,
options: &TransformOptions, options: &TransformOptions,
) -> Option<Self> { ) -> Option<Self> {
(options.target < TransformTarget::ES2020 || options.nullish_coalescing_operator.is_some()) (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 let no_document_all = options.assumptions.no_document_all
|| options.nullish_coalescing_operator.is_some_and(|o| o.loose); || options.nullish_coalescing_operator.is_some_and(|o| o.loose);
let vars = ast.new_vec(); 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; let assignment;
// skip creating extra reference when `left` is static // 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); reference = self.ast.copy(&logical_expr.left);
assignment = self.ast.copy(&logical_expr.left); assignment = self.ast.copy(&logical_expr.left);
} else { } else {

View file

@ -7,6 +7,7 @@
//! * <https://babel.dev/docs/presets> //! * <https://babel.dev/docs/presets>
//! * <https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts> //! * <https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts>
mod context;
mod es2015; mod es2015;
mod es2016; mod es2016;
mod es2019; mod es2019;
@ -25,14 +26,14 @@ use std::{cell::RefCell, rc::Rc};
use oxc_allocator::{Allocator, Vec}; use oxc_allocator::{Allocator, Vec};
use oxc_ast::{ast::*, AstBuilder, VisitMut}; use oxc_ast::{ast::*, AstBuilder, VisitMut};
use oxc_semantic::SymbolTable; use oxc_semantic::{ScopeTree, SymbolTable};
use oxc_span::SourceType; use oxc_span::SourceType;
use crate::{ use crate::{
es2015::ShorthandProperties, es2016::ExponentiationOperator, es2019::OptionalCatchBinding, context::TransformerCtx, es2015::ShorthandProperties, es2016::ExponentiationOperator,
es2020::NullishCoalescingOperator, es2021::LogicalAssignmentOperators, es2019::OptionalCatchBinding, es2020::NullishCoalescingOperator,
es2022::ClassStaticBlock, react_jsx::ReactJsx, regexp::RegexpFlags, typescript::TypeScript, es2021::LogicalAssignmentOperators, es2022::ClassStaticBlock, react_jsx::ReactJsx,
utils::CreateVars, regexp::RegexpFlags, typescript::TypeScript, utils::CreateVars,
}; };
pub use crate::{ pub use crate::{
@ -41,7 +42,6 @@ pub use crate::{
react_jsx::{ReactJsxOptions, ReactJsxRuntime}, react_jsx::{ReactJsxOptions, ReactJsxRuntime},
}; };
#[derive(Default)]
pub struct Transformer<'a> { pub struct Transformer<'a> {
#[allow(unused)] #[allow(unused)]
typescript: Option<TypeScript<'a>>, typescript: Option<TypeScript<'a>>,
@ -68,18 +68,24 @@ impl<'a> Transformer<'a> {
allocator: &'a Allocator, allocator: &'a Allocator,
source_type: SourceType, source_type: SourceType,
symbols: &Rc<RefCell<SymbolTable>>, symbols: &Rc<RefCell<SymbolTable>>,
scopes: &Rc<RefCell<ScopeTree>>,
options: TransformOptions, options: TransformOptions,
) -> Self { ) -> Self {
let ast = Rc::new(AstBuilder::new(allocator)); let ast = Rc::new(AstBuilder::new(allocator));
let ctx = TransformerCtx {
ast: Rc::clone(&ast),
symbols: Rc::clone(symbols),
scopes: Rc::clone(scopes),
};
Self { Self {
typescript: source_type.is_typescript().then(|| TypeScript::new(Rc::clone(&ast))), typescript: source_type.is_typescript().then(|| TypeScript::new(Rc::clone(&ast))),
react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), options)), react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), options)),
regexp_flags: RegexpFlags::new(Rc::clone(&ast), &options), regexp_flags: RegexpFlags::new(Rc::clone(&ast), &options),
es2022_class_static_block: es2022::ClassStaticBlock::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), 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), 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), es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
} }
} }

View file

@ -34,11 +34,13 @@ impl Tester {
let program = Parser::new(&self.allocator, source_text, self.source_type).parse().program; 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 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 symbols = Rc::new(RefCell::new(symbols));
let scopes = Rc::new(RefCell::new(scopes));
let program = self.allocator.alloc(program); 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) Codegen::<false>::new(source_text.len(), CodegenOptions).build(program)
} }

View file

@ -1,9 +1,11 @@
use std::mem; use std::mem;
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::ast::*;
use oxc_span::{Atom, Span}; use oxc_span::{Atom, Span};
use crate::context::TransformerCtx;
// TODO: // TODO:
// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L543> // <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 { pub fn generate_uid_based_on_node(expr: &Expression) -> Atom {
@ -52,7 +54,7 @@ impl GatherNodeParts for PrivateIdentifier {
} }
pub trait CreateVars<'a> { pub trait CreateVars<'a> {
fn ast(&self) -> &AstBuilder<'a>; fn ctx(&self) -> &TransformerCtx<'a>;
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'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() { if self.vars_mut().is_empty() {
return; 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 decls = mem::replace(self.vars_mut(), new_vec);
let kind = VariableDeclarationKind::Var; let kind = VariableDeclarationKind::Var;
let decl = 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)); let stmt = Statement::Declaration(Declaration::VariableDeclaration(decl));
stmts.insert(0, stmt); stmts.insert(0, stmt);
} }
@ -75,10 +77,10 @@ pub trait CreateVars<'a> {
// Add `var name` to scope // Add `var name` to scope
let binding_identifier = BindingIdentifier::new(Span::default(), name.clone()); let binding_identifier = BindingIdentifier::new(Span::default(), name.clone());
let binding_pattern_kind = self.ast().binding_pattern_identifier(binding_identifier); let binding_pattern_kind = self.ctx().ast.binding_pattern_identifier(binding_identifier);
let binding = self.ast().binding_pattern(binding_pattern_kind, None, false); let binding = self.ctx().ast.binding_pattern(binding_pattern_kind, None, false);
let kind = VariableDeclarationKind::Var; 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); self.vars_mut().push(decl);
name name
} }

View file

@ -213,11 +213,12 @@ impl Oxc {
// FIXME: this should not be duplicated with the linter semantic, // FIXME: this should not be duplicated with the linter semantic,
// we need to fix the API so symbols and scopes can be shared. // 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 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 symbols = Rc::new(RefCell::new(symbols));
let scopes = Rc::new(RefCell::new(scopes));
let options = let options =
TransformOptions { target: TransformTarget::ES2015, ..TransformOptions::default() }; 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); let program = allocator.alloc(program);

View file

@ -35,11 +35,12 @@ fn bench_transformer(criterion: &mut Criterion) {
let program = Parser::new(&allocator, source_text, source_type).parse().program; let program = Parser::new(&allocator, source_text, source_type).parse().program;
let semantic = let semantic =
SemanticBuilder::new(source_text, source_type).build(&program).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 symbols = Rc::new(RefCell::new(symbols));
let scopes = Rc::new(RefCell::new(scopes));
let program = allocator.alloc(program); let program = allocator.alloc(program);
let transform_options = TransformOptions::default(); 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)); .build(black_box(program));
allocator allocator
}); });

View file

@ -213,10 +213,11 @@ impl TestCase {
// Transform input.js // Transform input.js
let program = Parser::new(&allocator, &input, source_type).parse().program; let program = Parser::new(&allocator, &input, source_type).parse().program;
let semantic = SemanticBuilder::new(&input, source_type).build(&program).semantic; 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 symbols = Rc::new(RefCell::new(symbols));
let scopes = Rc::new(RefCell::new(scopes));
let program = allocator.alloc(program); 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); .build(program);
let transformed_code = Codegen::<false>::new(input.len(), CodegenOptions).build(program); let transformed_code = Codegen::<false>::new(input.len(), CodegenOptions).build(program);