mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
refactor(transformer): shared VarDeclarations (#6170)
First step towards #5049. Various transforms need to add a `var` statement at top of enclosing statement block. e.g.: ```js // Input a ??= b; ``` ```js // Output var _a; (_a = a) !== null && _a !== void 0 ? _a : (a = b); ``` Each of these transforms previously maintained it's own stack and added `var` statements individually. Share this functionality in a "common" utility transform which maintains a single stack to serve them all.
This commit is contained in:
parent
b92fe8401a
commit
21b08ba141
11 changed files with 228 additions and 263 deletions
37
crates/oxc_transformer/src/common/mod.rs
Normal file
37
crates/oxc_transformer/src/common/mod.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//! Utility transforms which are in common between other transforms.
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::TransformCtx;
|
||||
|
||||
mod var_declarations;
|
||||
|
||||
use var_declarations::VarDeclarations;
|
||||
pub use var_declarations::VarDeclarationsStore;
|
||||
|
||||
pub struct Common<'a, 'ctx> {
|
||||
var_declarations: VarDeclarations<'a, 'ctx>,
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Common<'a, 'ctx> {
|
||||
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { var_declarations: VarDeclarations::new(ctx) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for Common<'a, 'ctx> {
|
||||
#[inline] // Inline because it's no-op in release mode
|
||||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.var_declarations.exit_program(program, ctx);
|
||||
}
|
||||
|
||||
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.var_declarations.enter_statements(stmts, ctx);
|
||||
}
|
||||
|
||||
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.var_declarations.exit_statements(stmts, ctx);
|
||||
}
|
||||
}
|
||||
111
crates/oxc_transformer/src/common/var_declarations.rs
Normal file
111
crates/oxc_transformer/src/common/var_declarations.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
//! Utility transform to add `var` declarations to top of statement blocks.
|
||||
//!
|
||||
//! `VarDeclarationsStore` contains a stack of `Vec<VariableDeclarator>`s.
|
||||
//! It is stored on `TransformCtx`.
|
||||
//!
|
||||
//! `VarDeclarations` transform pushes an empty entry onto this stack when entering a statement block,
|
||||
//! and when exiting the block, writes a `var` statement to top of block containing the declarators.
|
||||
//!
|
||||
//! Other transforms can add declarators to the store by calling methods of `VarDeclarationsStore`:
|
||||
//!
|
||||
//! ```rs
|
||||
//! self.ctx.var_declarations.insert_declarator(name, symbol_id, None, ctx);
|
||||
//! ```
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, NONE};
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::symbol::SymbolId;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::{context::TransformCtx, helpers::stack::SparseStack};
|
||||
|
||||
/// Transform that maintains the stack of `Vec<VariableDeclarator>`s, and adds a `var` statement
|
||||
/// to top of a statement block if another transform has requested that.
|
||||
///
|
||||
/// Must run after all other transforms.
|
||||
pub struct VarDeclarations<'a, 'ctx> {
|
||||
ctx: &'ctx TransformCtx<'a>,
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> VarDeclarations<'a, 'ctx> {
|
||||
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { ctx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for VarDeclarations<'a, 'ctx> {
|
||||
#[inline] // Inline because it's no-op in release mode
|
||||
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
let declarators = self.ctx.var_declarations.declarators.borrow();
|
||||
debug_assert!(declarators.len() == 1);
|
||||
debug_assert!(declarators.last().is_none());
|
||||
}
|
||||
|
||||
fn enter_statements(
|
||||
&mut self,
|
||||
_stmts: &mut Vec<'a, Statement<'a>>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let mut declarators = self.ctx.var_declarations.declarators.borrow_mut();
|
||||
declarators.push(None);
|
||||
}
|
||||
|
||||
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
let mut declarators = self.ctx.var_declarations.declarators.borrow_mut();
|
||||
if let Some(declarators) = declarators.pop() {
|
||||
debug_assert!(!declarators.is_empty());
|
||||
let variable = ctx.ast.alloc_variable_declaration(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
declarators,
|
||||
false,
|
||||
);
|
||||
stmts.insert(0, Statement::VariableDeclaration(variable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Store for `VariableDeclarator`s to be added to enclosing statement block.
|
||||
pub struct VarDeclarationsStore<'a> {
|
||||
declarators: RefCell<SparseStack<Vec<'a, VariableDeclarator<'a>>>>,
|
||||
}
|
||||
|
||||
impl<'a> VarDeclarationsStore<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self { declarators: RefCell::new(SparseStack::new()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VarDeclarationsStore<'a> {
|
||||
/// Add a `VariableDeclarator` to be inserted at top of current enclosing statement block,
|
||||
/// given `name` and `symbol_id`.
|
||||
pub fn insert_declarator(
|
||||
&self,
|
||||
name: Atom<'a>,
|
||||
symbol_id: SymbolId,
|
||||
init: Option<Expression<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let ident = BindingIdentifier::new_with_symbol_id(SPAN, name, symbol_id);
|
||||
let ident = ctx.ast.binding_pattern_kind_from_binding_identifier(ident);
|
||||
let ident = ctx.ast.binding_pattern(ident, NONE, false);
|
||||
self.insert_declarator_binding_pattern(ident, init, ctx);
|
||||
}
|
||||
|
||||
/// Add a `VariableDeclarator` to be inserted at top of current enclosing statement block,
|
||||
/// given a `BindingPattern`.
|
||||
pub fn insert_declarator_binding_pattern(
|
||||
&self,
|
||||
ident: BindingPattern<'a>,
|
||||
init: Option<Expression<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let declarator =
|
||||
ctx.ast.variable_declarator(SPAN, VariableDeclarationKind::Var, ident, init, false);
|
||||
let mut declarators = self.declarators.borrow_mut();
|
||||
declarators.last_mut_or_init(|| ctx.ast.vec()).push(declarator);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,9 @@ use oxc_ast::{AstBuilder, Trivias};
|
|||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_span::SourceType;
|
||||
|
||||
use crate::{helpers::module_imports::ModuleImports, TransformOptions};
|
||||
use crate::{
|
||||
common::VarDeclarationsStore, helpers::module_imports::ModuleImports, TransformOptions,
|
||||
};
|
||||
|
||||
pub struct TransformCtx<'a> {
|
||||
errors: RefCell<Vec<OxcDiagnostic>>,
|
||||
|
|
@ -31,6 +33,8 @@ pub struct TransformCtx<'a> {
|
|||
// Helpers
|
||||
/// Manage import statement globally
|
||||
pub module_imports: ModuleImports<'a>,
|
||||
/// Manage inserting `var` statements globally
|
||||
pub var_declarations: VarDeclarationsStore<'a>,
|
||||
}
|
||||
|
||||
impl<'a> TransformCtx<'a> {
|
||||
|
|
@ -59,6 +63,7 @@ impl<'a> TransformCtx<'a> {
|
|||
source_text,
|
||||
trivias,
|
||||
module_imports: ModuleImports::new(allocator),
|
||||
var_declarations: VarDeclarationsStore::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ use oxc_span::SPAN;
|
|||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::helpers::stack::SparseStack;
|
||||
use crate::TransformCtx;
|
||||
|
||||
/// ES2016: Exponentiation Operator
|
||||
///
|
||||
|
|
@ -45,8 +45,8 @@ use crate::helpers::stack::SparseStack;
|
|||
/// * <https://babel.dev/docs/babel-plugin-transform-exponentiation-operator>
|
||||
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-exponentiation-operator>
|
||||
/// * <https://github.com/babel/babel/blob/main/packages/babel-helper-builder-binary-assignment-operator-visitor>
|
||||
pub struct ExponentiationOperator<'a> {
|
||||
var_declarations: SparseStack<Vec<'a, VariableDeclarator<'a>>>,
|
||||
pub struct ExponentiationOperator<'a, 'ctx> {
|
||||
ctx: &'ctx TransformCtx<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -55,44 +55,13 @@ struct Exploded<'a> {
|
|||
uid: Expression<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ExponentiationOperator<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self { var_declarations: SparseStack::new() }
|
||||
impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> {
|
||||
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { ctx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for ExponentiationOperator<'a> {
|
||||
#[inline] // Inline because it's no-op in release mode
|
||||
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
debug_assert!(self.var_declarations.len() == 1);
|
||||
debug_assert!(self.var_declarations.last().is_none());
|
||||
}
|
||||
|
||||
fn enter_statements(
|
||||
&mut self,
|
||||
_statements: &mut Vec<'a, Statement<'a>>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
self.var_declarations.push(None);
|
||||
}
|
||||
|
||||
fn exit_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if let Some(declarations) = self.var_declarations.pop() {
|
||||
debug_assert!(!declarations.is_empty());
|
||||
let variable = ctx.ast.alloc_variable_declaration(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
declarations,
|
||||
false,
|
||||
);
|
||||
statements.insert(0, Statement::VariableDeclaration(variable));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for ExponentiationOperator<'a, 'ctx> {
|
||||
// NOTE: Bail bigint arguments to `Math.pow`, which are runtime errors.
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
match expr {
|
||||
|
|
@ -139,7 +108,7 @@ impl<'a> Traverse<'a> for ExponentiationOperator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ExponentiationOperator<'a> {
|
||||
impl<'a, 'ctx> ExponentiationOperator<'a, 'ctx> {
|
||||
fn clone_expression(expr: &Expression<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
|
||||
match expr {
|
||||
Expression::Identifier(ident) => ctx.ast.expression_from_identifier_reference(
|
||||
|
|
@ -325,17 +294,8 @@ impl<'a> ExponentiationOperator<'a> {
|
|||
ctx.generate_uid_in_current_scope(name, SymbolFlags::FunctionScopedVariable);
|
||||
let symbol_name = ctx.ast.atom(ctx.symbols().get_name(symbol_id));
|
||||
|
||||
{
|
||||
// var _name;
|
||||
let binding_identifier =
|
||||
BindingIdentifier::new_with_symbol_id(SPAN, symbol_name.clone(), symbol_id);
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let id = ctx.ast.binding_pattern_kind_from_binding_identifier(binding_identifier);
|
||||
let id = ctx.ast.binding_pattern(id, NONE, false);
|
||||
self.var_declarations
|
||||
.last_mut_or_init(|| ctx.ast.vec())
|
||||
.push(ctx.ast.variable_declarator(SPAN, kind, id, None, false));
|
||||
}
|
||||
// var _name;
|
||||
self.ctx.var_declarations.insert_declarator(symbol_name.clone(), symbol_id, None, ctx);
|
||||
|
||||
let ident =
|
||||
ctx.create_reference_id(SPAN, symbol_name, Some(symbol_id), ReferenceFlags::Read);
|
||||
|
|
|
|||
|
|
@ -3,51 +3,25 @@ mod options;
|
|||
|
||||
pub use exponentiation_operator::ExponentiationOperator;
|
||||
pub use options::ES2016Options;
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
pub struct ES2016<'a> {
|
||||
use crate::TransformCtx;
|
||||
|
||||
pub struct ES2016<'a, 'ctx> {
|
||||
options: ES2016Options,
|
||||
|
||||
// Plugins
|
||||
exponentiation_operator: ExponentiationOperator<'a>,
|
||||
exponentiation_operator: ExponentiationOperator<'a, 'ctx>,
|
||||
}
|
||||
|
||||
impl<'a> ES2016<'a> {
|
||||
pub fn new(options: ES2016Options) -> Self {
|
||||
Self { exponentiation_operator: ExponentiationOperator::new(), options }
|
||||
impl<'a, 'ctx> ES2016<'a, 'ctx> {
|
||||
pub fn new(options: ES2016Options, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { exponentiation_operator: ExponentiationOperator::new(ctx), options }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for ES2016<'a> {
|
||||
#[inline] // Inline because it's no-op in release mode
|
||||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.exponentiation_operator {
|
||||
self.exponentiation_operator.exit_program(program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.options.exponentiation_operator {
|
||||
self.exponentiation_operator.enter_statements(statements, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.options.exponentiation_operator {
|
||||
self.exponentiation_operator.exit_statements(statements, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for ES2016<'a, 'ctx> {
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.exponentiation_operator {
|
||||
self.exponentiation_operator.enter_expression(expr, ctx);
|
||||
|
|
|
|||
|
|
@ -3,51 +3,25 @@ mod options;
|
|||
|
||||
pub use nullish_coalescing_operator::NullishCoalescingOperator;
|
||||
pub use options::ES2020Options;
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
pub struct ES2020<'a> {
|
||||
use crate::TransformCtx;
|
||||
|
||||
pub struct ES2020<'a, 'ctx> {
|
||||
options: ES2020Options,
|
||||
|
||||
// Plugins
|
||||
nullish_coalescing_operator: NullishCoalescingOperator<'a>,
|
||||
nullish_coalescing_operator: NullishCoalescingOperator<'a, 'ctx>,
|
||||
}
|
||||
|
||||
impl<'a> ES2020<'a> {
|
||||
pub fn new(options: ES2020Options) -> Self {
|
||||
Self { nullish_coalescing_operator: NullishCoalescingOperator::new(), options }
|
||||
impl<'a, 'ctx> ES2020<'a, 'ctx> {
|
||||
pub fn new(options: ES2020Options, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { nullish_coalescing_operator: NullishCoalescingOperator::new(ctx), options }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for ES2020<'a> {
|
||||
#[inline] // Inline because it's no-op in release mode
|
||||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.nullish_coalescing_operator {
|
||||
self.nullish_coalescing_operator.exit_program(program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.options.nullish_coalescing_operator {
|
||||
self.nullish_coalescing_operator.enter_statements(statements, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.options.nullish_coalescing_operator {
|
||||
self.nullish_coalescing_operator.exit_statements(statements, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for ES2020<'a, 'ctx> {
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.nullish_coalescing_operator {
|
||||
self.nullish_coalescing_operator.enter_expression(expr, ctx);
|
||||
|
|
|
|||
|
|
@ -28,57 +28,26 @@
|
|||
//! * Babel plugin implementation: <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-nullish-coalescing-operator>
|
||||
//! * Nullish coalescing TC39 proposal: <https://github.com/tc39-transfer/proposal-nullish-coalescing>
|
||||
|
||||
use oxc_allocator::{CloneIn, Vec};
|
||||
use oxc_allocator::CloneIn;
|
||||
use oxc_ast::{ast::*, NONE};
|
||||
use oxc_semantic::{ReferenceFlags, ScopeFlags, ScopeId, SymbolFlags};
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator};
|
||||
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};
|
||||
|
||||
use crate::helpers::stack::SparseStack;
|
||||
use crate::TransformCtx;
|
||||
|
||||
pub struct NullishCoalescingOperator<'a> {
|
||||
var_declarations: SparseStack<Vec<'a, VariableDeclarator<'a>>>,
|
||||
pub struct NullishCoalescingOperator<'a, 'ctx> {
|
||||
ctx: &'ctx TransformCtx<'a>,
|
||||
}
|
||||
|
||||
impl<'a> NullishCoalescingOperator<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self { var_declarations: SparseStack::new() }
|
||||
impl<'a, 'ctx> NullishCoalescingOperator<'a, 'ctx> {
|
||||
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { ctx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> {
|
||||
#[inline] // Inline because it's no-op in release mode
|
||||
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
debug_assert!(self.var_declarations.len() == 1);
|
||||
debug_assert!(self.var_declarations.last().is_none());
|
||||
}
|
||||
|
||||
fn enter_statements(
|
||||
&mut self,
|
||||
_stmts: &mut Vec<'a, Statement<'a>>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
self.var_declarations.push(None);
|
||||
}
|
||||
|
||||
fn exit_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if let Some(declarations) = self.var_declarations.pop() {
|
||||
debug_assert!(!declarations.is_empty());
|
||||
let variable = ctx.ast.alloc_variable_declaration(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
declarations,
|
||||
false,
|
||||
);
|
||||
statements.insert(0, Statement::VariableDeclaration(variable));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for NullishCoalescingOperator<'a, 'ctx> {
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
// left ?? right
|
||||
if !matches!(expr, Expression::LogicalExpression(logical_expr) if logical_expr.operator == LogicalOperator::Coalesce)
|
||||
|
|
@ -156,17 +125,14 @@ impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> {
|
|||
// `(x) => x;` -> `((x) => x)();`
|
||||
new_expr = ctx.ast.expression_call(SPAN, arrow_function, NONE, ctx.ast.vec(), false);
|
||||
} else {
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
self.var_declarations
|
||||
.last_mut_or_init(|| ctx.ast.vec())
|
||||
.push(ctx.ast.variable_declarator(SPAN, kind, id, None, false));
|
||||
self.ctx.var_declarations.insert_declarator_binding_pattern(id, None, ctx);
|
||||
}
|
||||
|
||||
*expr = new_expr;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NullishCoalescingOperator<'a> {
|
||||
impl<'a, 'ctx> NullishCoalescingOperator<'a, 'ctx> {
|
||||
fn clone_expression(expr: &Expression<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
|
||||
match expr {
|
||||
Expression::Identifier(ident) => ctx.ast.expression_from_identifier_reference(
|
||||
|
|
|
|||
|
|
@ -53,57 +53,26 @@
|
|||
//! * Babel plugin implementation: <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-logical-assignment-operators>
|
||||
//! * Logical Assignment TC39 proposal: <https://github.com/tc39/proposal-logical-assignment>
|
||||
|
||||
use oxc_allocator::{CloneIn, Vec};
|
||||
use oxc_ast::{ast::*, NONE};
|
||||
use oxc_allocator::CloneIn;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_semantic::{ReferenceFlags, SymbolFlags};
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::operator::{AssignmentOperator, LogicalOperator};
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
use crate::helpers::stack::SparseStack;
|
||||
use crate::TransformCtx;
|
||||
|
||||
pub struct LogicalAssignmentOperators<'a> {
|
||||
var_declarations: SparseStack<Vec<'a, VariableDeclarator<'a>>>,
|
||||
pub struct LogicalAssignmentOperators<'a, 'ctx> {
|
||||
ctx: &'ctx TransformCtx<'a>,
|
||||
}
|
||||
|
||||
impl<'a> LogicalAssignmentOperators<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self { var_declarations: SparseStack::new() }
|
||||
impl<'a, 'ctx> LogicalAssignmentOperators<'a, 'ctx> {
|
||||
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { ctx }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> {
|
||||
#[inline] // Inline because it's no-op in release mode
|
||||
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||
debug_assert!(self.var_declarations.len() == 1);
|
||||
debug_assert!(self.var_declarations.last().is_none());
|
||||
}
|
||||
|
||||
fn enter_statements(
|
||||
&mut self,
|
||||
_stmts: &mut Vec<'a, Statement<'a>>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
self.var_declarations.push(None);
|
||||
}
|
||||
|
||||
fn exit_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if let Some(declarations) = self.var_declarations.pop() {
|
||||
debug_assert!(!declarations.is_empty());
|
||||
let variable = ctx.ast.alloc_variable_declaration(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
declarations,
|
||||
false,
|
||||
);
|
||||
statements.insert(0, Statement::VariableDeclaration(variable));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for LogicalAssignmentOperators<'a, 'ctx> {
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
let Expression::AssignmentExpression(assignment_expr) = expr else { return };
|
||||
|
||||
|
|
@ -154,7 +123,7 @@ impl<'a> Traverse<'a> for LogicalAssignmentOperators<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> LogicalAssignmentOperators<'a> {
|
||||
impl<'a, 'ctx> LogicalAssignmentOperators<'a, 'ctx> {
|
||||
fn convert_identifier(
|
||||
ident: &IdentifierReference<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
|
|
@ -390,15 +359,7 @@ impl<'a> LogicalAssignmentOperators<'a> {
|
|||
let symbol_name = ctx.ast.atom(ctx.symbols().get_name(symbol_id));
|
||||
|
||||
// var _name;
|
||||
let binding_identifier =
|
||||
BindingIdentifier::new_with_symbol_id(SPAN, symbol_name.clone(), symbol_id);
|
||||
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let id = ctx.ast.binding_pattern_kind_from_binding_identifier(binding_identifier);
|
||||
let id = ctx.ast.binding_pattern(id, NONE, false);
|
||||
self.var_declarations
|
||||
.last_mut_or_init(|| ctx.ast.vec())
|
||||
.push(ctx.ast.variable_declarator(SPAN, kind, id, None, false));
|
||||
self.ctx.var_declarations.insert_declarator(symbol_name.clone(), symbol_id, None, ctx);
|
||||
|
||||
// _name = name
|
||||
Some(ctx.create_reference_id(SPAN, symbol_name, Some(symbol_id), ReferenceFlags::Write))
|
||||
|
|
|
|||
|
|
@ -3,51 +3,25 @@ mod options;
|
|||
|
||||
pub use logical_assignment_operators::LogicalAssignmentOperators;
|
||||
pub use options::ES2021Options;
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
||||
pub struct ES2021<'a> {
|
||||
use crate::TransformCtx;
|
||||
|
||||
pub struct ES2021<'a, 'ctx> {
|
||||
options: ES2021Options,
|
||||
|
||||
// Plugins
|
||||
logical_assignment_operators: LogicalAssignmentOperators<'a>,
|
||||
logical_assignment_operators: LogicalAssignmentOperators<'a, 'ctx>,
|
||||
}
|
||||
|
||||
impl<'a> ES2021<'a> {
|
||||
pub fn new(options: ES2021Options) -> Self {
|
||||
Self { logical_assignment_operators: LogicalAssignmentOperators::new(), options }
|
||||
impl<'a, 'ctx> ES2021<'a, 'ctx> {
|
||||
pub fn new(options: ES2021Options, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { logical_assignment_operators: LogicalAssignmentOperators::new(ctx), options }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Traverse<'a> for ES2021<'a> {
|
||||
#[inline] // Inline because it's no-op in release mode
|
||||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.logical_assignment_operators {
|
||||
self.logical_assignment_operators.exit_program(program, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.options.logical_assignment_operators {
|
||||
self.logical_assignment_operators.enter_statements(statements, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.options.logical_assignment_operators {
|
||||
self.logical_assignment_operators.exit_statements(statements, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for ES2021<'a, 'ctx> {
|
||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.logical_assignment_operators {
|
||||
self.logical_assignment_operators.enter_expression(expr, ctx);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
//! * <https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts>
|
||||
|
||||
// Core
|
||||
mod common;
|
||||
mod compiler_assumptions;
|
||||
mod context;
|
||||
mod options;
|
||||
|
|
@ -32,6 +33,7 @@ mod helpers {
|
|||
|
||||
use std::path::Path;
|
||||
|
||||
use common::Common;
|
||||
use es2016::ES2016;
|
||||
use es2018::ES2018;
|
||||
use es2019::ES2019;
|
||||
|
|
@ -91,13 +93,14 @@ impl<'a> Transformer<'a> {
|
|||
let mut transformer = TransformerImpl {
|
||||
x0_typescript: TypeScript::new(self.options.typescript, &self.ctx),
|
||||
x1_react: React::new(self.options.react, &self.ctx),
|
||||
x2_es2021: ES2021::new(self.options.es2021),
|
||||
x2_es2020: ES2020::new(self.options.es2020),
|
||||
x2_es2021: ES2021::new(self.options.es2021, &self.ctx),
|
||||
x2_es2020: ES2020::new(self.options.es2020, &self.ctx),
|
||||
x2_es2019: ES2019::new(self.options.es2019),
|
||||
x2_es2018: ES2018::new(self.options.es2018),
|
||||
x2_es2016: ES2016::new(self.options.es2016),
|
||||
x2_es2016: ES2016::new(self.options.es2016, &self.ctx),
|
||||
x3_es2015: ES2015::new(self.options.es2015),
|
||||
x4_regexp: RegExp::new(self.options.regexp, &self.ctx),
|
||||
common: Common::new(&self.ctx),
|
||||
};
|
||||
|
||||
let (symbols, scopes) = traverse_mut(&mut transformer, allocator, program, symbols, scopes);
|
||||
|
|
@ -109,13 +112,14 @@ struct TransformerImpl<'a, 'ctx> {
|
|||
// NOTE: all callbacks must run in order.
|
||||
x0_typescript: TypeScript<'a, 'ctx>,
|
||||
x1_react: React<'a, 'ctx>,
|
||||
x2_es2021: ES2021<'a>,
|
||||
x2_es2020: ES2020<'a>,
|
||||
x2_es2021: ES2021<'a, 'ctx>,
|
||||
x2_es2020: ES2020<'a, 'ctx>,
|
||||
x2_es2019: ES2019,
|
||||
x2_es2018: ES2018,
|
||||
x2_es2016: ES2016<'a>,
|
||||
x2_es2016: ES2016<'a, 'ctx>,
|
||||
x3_es2015: ES2015<'a>,
|
||||
x4_regexp: RegExp<'a, 'ctx>,
|
||||
common: Common<'a, 'ctx>,
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
||||
|
|
@ -127,10 +131,8 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
|||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.x1_react.exit_program(program, ctx);
|
||||
self.x0_typescript.exit_program(program, ctx);
|
||||
self.x2_es2021.exit_program(program, ctx);
|
||||
self.x2_es2020.exit_program(program, ctx);
|
||||
self.x2_es2016.exit_program(program, ctx);
|
||||
self.x3_es2015.exit_program(program, ctx);
|
||||
self.common.exit_program(program, ctx);
|
||||
}
|
||||
|
||||
// ALPHASORT
|
||||
|
|
@ -300,11 +302,9 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
|||
}
|
||||
|
||||
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.common.enter_statements(stmts, ctx);
|
||||
self.x0_typescript.enter_statements(stmts, ctx);
|
||||
self.x1_react.enter_statements(stmts, ctx);
|
||||
self.x2_es2021.enter_statements(stmts, ctx);
|
||||
self.x2_es2020.enter_statements(stmts, ctx);
|
||||
self.x2_es2016.enter_statements(stmts, ctx);
|
||||
}
|
||||
|
||||
fn exit_arrow_function_expression(
|
||||
|
|
@ -334,9 +334,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
|||
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.x0_typescript.exit_statements(stmts, ctx);
|
||||
self.x1_react.exit_statements(stmts, ctx);
|
||||
self.x2_es2021.exit_statements(stmts, ctx);
|
||||
self.x2_es2020.exit_statements(stmts, ctx);
|
||||
self.x2_es2016.exit_statements(stmts, ctx);
|
||||
self.common.exit_statements(stmts, ctx);
|
||||
}
|
||||
|
||||
fn enter_tagged_template_expression(
|
||||
|
|
|
|||
|
|
@ -1456,7 +1456,12 @@ after transform: SymbolId(21): [ReferenceId(132), ReferenceId(133), ReferenceId(
|
|||
rebuilt : SymbolId(14): [ReferenceId(123), ReferenceId(127)]
|
||||
|
||||
* logical-assignment/null-coalescing/input.js
|
||||
x Output mismatch
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(17): [ReferenceId(86), ReferenceId(87), ReferenceId(90)]
|
||||
rebuilt : SymbolId(10): [ReferenceId(73), ReferenceId(79)]
|
||||
Symbol reference IDs mismatch:
|
||||
after transform: SymbolId(20): [ReferenceId(94), ReferenceId(95), ReferenceId(98)]
|
||||
rebuilt : SymbolId(13): [ReferenceId(88), ReferenceId(94)]
|
||||
|
||||
* logical-assignment/null-coalescing-without-other/input.js
|
||||
Symbol reference IDs mismatch:
|
||||
|
|
|
|||
Loading…
Reference in a new issue