mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(transformer): add utils to make logical_assignment_operators pass (#1017)
This commit is contained in:
parent
3f06335172
commit
dfee8539f0
12 changed files with 340 additions and 95 deletions
|
|
@ -0,0 +1,82 @@
|
|||
use crate::ast::*;
|
||||
use oxc_span::Atom;
|
||||
|
||||
// TODO: <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L61>
|
||||
pub trait GatherNodeParts {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F);
|
||||
}
|
||||
|
||||
impl<'a> GatherNodeParts for Expression<'a> {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
match self {
|
||||
Self::Identifier(ident) => f(ident.name.clone()),
|
||||
Self::MemberExpression(expr) => expr.gather(f),
|
||||
Self::AssignmentExpression(expr) => expr.left.gather(f),
|
||||
Self::UpdateExpression(expr) => expr.argument.gather(f),
|
||||
Self::StringLiteral(lit) => lit.gather(f),
|
||||
_ => f(Atom::from("ref")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GatherNodeParts for MemberExpression<'a> {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
match self {
|
||||
MemberExpression::ComputedMemberExpression(expr) => {
|
||||
expr.object.gather(f);
|
||||
expr.expression.gather(f);
|
||||
}
|
||||
MemberExpression::StaticMemberExpression(expr) => {
|
||||
expr.object.gather(f);
|
||||
expr.property.gather(f);
|
||||
}
|
||||
MemberExpression::PrivateFieldExpression(expr) => {
|
||||
expr.object.gather(f);
|
||||
expr.field.gather(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GatherNodeParts for AssignmentTarget<'a> {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
match self {
|
||||
AssignmentTarget::SimpleAssignmentTarget(t) => t.gather(f),
|
||||
AssignmentTarget::AssignmentTargetPattern(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GatherNodeParts for SimpleAssignmentTarget<'a> {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
match self {
|
||||
Self::AssignmentTargetIdentifier(ident) => ident.gather(f),
|
||||
Self::MemberAssignmentTarget(expr) => expr.gather(f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GatherNodeParts for IdentifierReference {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
f(self.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl GatherNodeParts for IdentifierName {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
f(self.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl GatherNodeParts for PrivateIdentifier {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
f(self.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl GatherNodeParts for StringLiteral {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
f(self.value.clone());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
//! [ECMA262 Syntax-Directed Operations](https://tc39.es/ecma262/#sec-syntax-directed-operations)
|
||||
|
||||
mod bound_names;
|
||||
mod gather_node_parts;
|
||||
mod is_simple_parameter_list;
|
||||
mod private_bound_identifiers;
|
||||
mod prop_name;
|
||||
|
||||
pub use self::{
|
||||
bound_names::BoundNames, is_simple_parameter_list::IsSimpleParameterList,
|
||||
bound_names::BoundNames, gather_node_parts::GatherNodeParts,
|
||||
is_simple_parameter_list::IsSimpleParameterList,
|
||||
private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1182,9 +1182,12 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ArrayExpression<'a> {
|
|||
impl<'a, const MINIFY: bool> GenExpr<MINIFY> for ObjectExpression<'a> {
|
||||
fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, _precedence: Precedence, ctx: Context) {
|
||||
let n = p.code_len();
|
||||
let is_multi_line = !self.properties.is_empty();
|
||||
p.wrap(p.start_of_stmt == n || p.start_of_arrow_expr == n, |p| {
|
||||
p.print(b'{');
|
||||
p.indent();
|
||||
if is_multi_line {
|
||||
p.indent();
|
||||
}
|
||||
for (i, item) in self.properties.iter().enumerate() {
|
||||
if i != 0 {
|
||||
p.print_comma();
|
||||
|
|
@ -1193,9 +1196,11 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for ObjectExpression<'a> {
|
|||
p.print_indent();
|
||||
item.gen(p, ctx);
|
||||
}
|
||||
p.print_soft_newline();
|
||||
p.dedent();
|
||||
p.print_indent();
|
||||
if is_multi_line {
|
||||
p.print_soft_newline();
|
||||
p.dedent();
|
||||
p.print_indent();
|
||||
}
|
||||
p.print(b'}');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::hash::BuildHasherDefault;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use oxc_ast::{ast::Expression, syntax_directed_operations::GatherNodeParts};
|
||||
use oxc_index::IndexVec;
|
||||
use oxc_span::Atom;
|
||||
pub use oxc_syntax::scope::{ScopeFlags, ScopeId};
|
||||
|
|
@ -70,6 +71,10 @@ impl ScopeTree {
|
|||
self.get_binding(self.root_scope_id(), name)
|
||||
}
|
||||
|
||||
pub fn has_binding(&self, scope_id: ScopeId, name: &Atom) -> bool {
|
||||
self.bindings[scope_id].get(name).is_some()
|
||||
}
|
||||
|
||||
pub fn get_binding(&self, scope_id: ScopeId, name: &Atom) -> Option<SymbolId> {
|
||||
self.bindings[scope_id].get(name).copied()
|
||||
}
|
||||
|
|
@ -96,7 +101,7 @@ impl ScopeTree {
|
|||
scope_id
|
||||
}
|
||||
|
||||
pub(crate) fn add_binding(&mut self, scope_id: ScopeId, name: Atom, symbol_id: SymbolId) {
|
||||
pub fn add_binding(&mut self, scope_id: ScopeId, name: Atom, symbol_id: SymbolId) {
|
||||
self.bindings[scope_id].insert(name, symbol_id);
|
||||
}
|
||||
|
||||
|
|
@ -124,4 +129,24 @@ impl ScopeTree {
|
|||
) -> &mut UnresolvedReferences {
|
||||
&mut self.unresolved_references[scope_id]
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L543>
|
||||
pub fn generate_uid_based_on_node(&self, expr: &Expression) -> Atom {
|
||||
let mut parts = std::vec::Vec::with_capacity(1);
|
||||
expr.gather(&mut |part| parts.push(part));
|
||||
let name = parts.join("$");
|
||||
let name = name.trim_start_matches('_');
|
||||
for i in 0.. {
|
||||
let name = Self::generate_uid(name, i);
|
||||
if !self.has_binding(ScopeId::new(0), &name) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn generate_uid(name: &str, i: i32) -> Atom {
|
||||
Atom::from(if i > 1 { format!("_{name}{i}") } else { format!("_{name}") })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,10 +127,13 @@ impl SymbolTable {
|
|||
pub fn is_static(&self, expr: &Expression) -> bool {
|
||||
match expr {
|
||||
Expression::ThisExpression(_) | Expression::Super(_) => true,
|
||||
Expression::Identifier(ident)
|
||||
if ident.reference_id.get().is_some_and(|id| self.has_binding(id)) =>
|
||||
{
|
||||
true
|
||||
Expression::Identifier(ident) => {
|
||||
ident.reference_id.get().map_or(false, |reference_id| {
|
||||
self.get_reference(reference_id).symbol_id().map_or_else(
|
||||
|| self.has_binding(reference_id),
|
||||
|symbol_id| self.get_resolved_references(symbol_id).all(|r| !r.is_write()),
|
||||
)
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use oxc_ast::AstBuilder;
|
||||
use oxc_semantic::{ScopeTree, SymbolTable};
|
||||
use oxc_semantic::{ScopeId, ScopeTree, SymbolId, SymbolTable};
|
||||
use oxc_span::Atom;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TransformerCtx<'a> {
|
||||
|
|
@ -9,3 +13,18 @@ pub struct TransformerCtx<'a> {
|
|||
pub symbols: Rc<RefCell<SymbolTable>>,
|
||||
pub scopes: Rc<RefCell<ScopeTree>>,
|
||||
}
|
||||
|
||||
impl<'a> TransformerCtx<'a> {
|
||||
pub fn symbols(&self) -> Ref<SymbolTable> {
|
||||
self.symbols.borrow()
|
||||
}
|
||||
|
||||
pub fn scopes(&self) -> Ref<ScopeTree> {
|
||||
self.scopes.borrow()
|
||||
}
|
||||
|
||||
pub fn add_binding(&self, name: Atom) {
|
||||
// TODO: use the correct scope and symbol id
|
||||
self.scopes.borrow_mut().add_binding(ScopeId::new(0), name, SymbolId::new(0));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,9 +228,8 @@ impl<'a> ExponentiationOperator<'a> {
|
|||
expr: Expression<'a>,
|
||||
nodes: &mut Vec<'a, Expression<'a>>,
|
||||
) -> Expression<'a> {
|
||||
let name = self.create_new_var(&expr);
|
||||
let ident = self.create_new_var(&expr);
|
||||
// Add new reference `_name = name` to nodes
|
||||
let ident = IdentifierReference::new(Span::default(), name);
|
||||
let target = self.ast.simple_assignment_target_identifier(ident.clone());
|
||||
let target = AssignmentTarget::SimpleAssignmentTarget(target);
|
||||
let op = AssignmentOperator::Assign;
|
||||
|
|
@ -251,7 +250,7 @@ fn test() {
|
|||
|
||||
let tests = &[(
|
||||
"let x = {}; let y = 0; let z = 0; x[z++] **= y;",
|
||||
"var _ref; let x = {}; let y = 0; let z = 0; _ref = z++,x[_ref] = Math.pow(x[_ref], y);",
|
||||
"var _z; let x = {}; let y = 0; let z = 0; _z = z++,x[_z] = Math.pow(x[_z], y);",
|
||||
)];
|
||||
|
||||
Tester::new("test.js", options).test(tests);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ pub struct NullishCoalescingOperator<'a> {
|
|||
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
ctx: TransformerCtx<'a>,
|
||||
|
||||
vars: Vec<'a, VariableDeclarator<'a>>,
|
||||
}
|
||||
|
||||
|
|
@ -70,12 +71,11 @@ impl<'a> NullishCoalescingOperator<'a> {
|
|||
let assignment;
|
||||
|
||||
// skip creating extra reference when `left` is static
|
||||
if self.ctx.symbols.borrow().is_static(&logical_expr.left) {
|
||||
if self.ctx.symbols().is_static(&logical_expr.left) {
|
||||
reference = self.ast.copy(&logical_expr.left);
|
||||
assignment = self.ast.copy(&logical_expr.left);
|
||||
} else {
|
||||
let name = self.create_new_var(&logical_expr.left);
|
||||
let ident = IdentifierReference::new(span, name);
|
||||
let ident = self.create_new_var(&logical_expr.left);
|
||||
reference = self.ast.identifier_reference_expression(ident.clone());
|
||||
let left = AssignmentTarget::SimpleAssignmentTarget(
|
||||
self.ast.simple_assignment_target_identifier(ident),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::operator::{AssignmentOperator, LogicalOperator};
|
||||
|
||||
use crate::options::{TransformOptions, TransformTarget};
|
||||
use crate::{
|
||||
context::TransformerCtx,
|
||||
options::{TransformOptions, TransformTarget},
|
||||
utils::CreateVars,
|
||||
};
|
||||
|
||||
/// ES2021: Logical Assignment Operators
|
||||
///
|
||||
|
|
@ -13,17 +18,39 @@ use crate::options::{TransformOptions, TransformTarget};
|
|||
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-logical-assignment-operators>
|
||||
pub struct LogicalAssignmentOperators<'a> {
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
ctx: TransformerCtx<'a>,
|
||||
|
||||
vars: Vec<'a, VariableDeclarator<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> CreateVars<'a> for LogicalAssignmentOperators<'a> {
|
||||
fn ctx(&self) -> &TransformerCtx<'a> {
|
||||
&self.ctx
|
||||
}
|
||||
|
||||
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> {
|
||||
&mut self.vars
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LogicalAssignmentOperators<'a> {
|
||||
pub fn new(ast: Rc<AstBuilder<'a>>, options: &TransformOptions) -> Option<Self> {
|
||||
(options.target < TransformTarget::ES2021 || options.logical_assignment_operators)
|
||||
.then(|| Self { ast })
|
||||
pub fn new(
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
ctx: TransformerCtx<'a>,
|
||||
options: &TransformOptions,
|
||||
) -> Option<Self> {
|
||||
(options.target < TransformTarget::ES2021 || options.logical_assignment_operators).then(
|
||||
|| {
|
||||
let vars = ast.new_vec();
|
||||
Self { ast, ctx, vars }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn transform_expression<'b>(&mut self, expr: &'b mut Expression<'a>) {
|
||||
let Expression::AssignmentExpression(assignment_expr) = expr else { return };
|
||||
|
||||
// `&&=` `||=` `??=`
|
||||
let operator = match assignment_expr.operator {
|
||||
AssignmentOperator::LogicalAnd => LogicalOperator::And,
|
||||
AssignmentOperator::LogicalOr => LogicalOperator::Or,
|
||||
|
|
@ -31,18 +58,135 @@ impl<'a> LogicalAssignmentOperators<'a> {
|
|||
_ => return,
|
||||
};
|
||||
|
||||
// Create the left hand side
|
||||
// a || (a = b)
|
||||
// ^ ^
|
||||
let left1: AssignmentTarget<'a> = self.ast.copy(&assignment_expr.left);
|
||||
let left2 = match &assignment_expr.left {
|
||||
// `a &&= c` -> `a && (a = c);`
|
||||
// ^ ^ assign_target
|
||||
// ^ left_expr
|
||||
|
||||
let left_expr: Expression<'a>;
|
||||
let assign_target: SimpleAssignmentTarget<'a>;
|
||||
|
||||
// TODO: refactor this block, add tests, cover private identifier
|
||||
match &assignment_expr.left {
|
||||
AssignmentTarget::SimpleAssignmentTarget(target) => match target {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => {
|
||||
self.ast.identifier_reference_expression((*ident).clone())
|
||||
left_expr = self.ast.identifier_reference_expression((*ident).clone());
|
||||
assign_target = self.ast.simple_assignment_target_identifier((*ident).clone());
|
||||
}
|
||||
SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) => {
|
||||
let member_expr = self.ast.copy(&**member_expr);
|
||||
self.ast.member_expression(member_expr)
|
||||
let span = Span::default();
|
||||
let op = AssignmentOperator::Assign;
|
||||
|
||||
// `a.b &&= c` -> `var _a; (_a = a).b && (_a.b = c)`
|
||||
match &**member_expr {
|
||||
MemberExpression::StaticMemberExpression(static_expr) => {
|
||||
if let Some(ident) = self.maybe_generate_memoised(&static_expr.object) {
|
||||
let right = self.ast.copy(&static_expr.object);
|
||||
let mut expr = self.ast.copy(static_expr);
|
||||
let target = AssignmentTarget::SimpleAssignmentTarget(
|
||||
self.ast.simple_assignment_target_identifier(ident.clone()),
|
||||
);
|
||||
expr.object =
|
||||
self.ast.assignment_expression(span, op, target, right);
|
||||
left_expr = self.ast.member_expression(
|
||||
MemberExpression::StaticMemberExpression(expr),
|
||||
);
|
||||
|
||||
let mut expr = self.ast.copy(static_expr);
|
||||
expr.object = self.ast.identifier_reference_expression(ident);
|
||||
assign_target =
|
||||
self.ast.simple_assignment_target_member_expression(
|
||||
MemberExpression::StaticMemberExpression(expr),
|
||||
);
|
||||
} else {
|
||||
left_expr = self.ast.member_expression(
|
||||
MemberExpression::StaticMemberExpression(
|
||||
self.ast.copy(static_expr),
|
||||
),
|
||||
);
|
||||
assign_target = SimpleAssignmentTarget::MemberAssignmentTarget(
|
||||
self.ast.copy(member_expr),
|
||||
);
|
||||
};
|
||||
}
|
||||
// `a[b.y] &&= c;` ->
|
||||
// `var _a, _b$y; (_a = a)[_b$y = b.y] && (_a[_b$y] = c);`
|
||||
MemberExpression::ComputedMemberExpression(computed_expr) => {
|
||||
if let Some(ident) = self.maybe_generate_memoised(&computed_expr.object)
|
||||
{
|
||||
let property_ident =
|
||||
self.maybe_generate_memoised(&computed_expr.expression);
|
||||
|
||||
let right = self.ast.copy(&computed_expr.object);
|
||||
let mut expr = self.ast.copy(computed_expr);
|
||||
let target = AssignmentTarget::SimpleAssignmentTarget(
|
||||
self.ast.simple_assignment_target_identifier(ident.clone()),
|
||||
);
|
||||
expr.object =
|
||||
self.ast.assignment_expression(span, op, target, right);
|
||||
if let Some(property_ident) = &property_ident {
|
||||
let left = AssignmentTarget::SimpleAssignmentTarget(
|
||||
self.ast.simple_assignment_target_identifier(
|
||||
property_ident.clone(),
|
||||
),
|
||||
);
|
||||
let right = self.ast.copy(&computed_expr.expression);
|
||||
expr.expression =
|
||||
self.ast.assignment_expression(span, op, left, right);
|
||||
}
|
||||
left_expr = self.ast.member_expression(
|
||||
MemberExpression::ComputedMemberExpression(expr),
|
||||
);
|
||||
|
||||
// `(_a[_b$y] = c)` part
|
||||
let mut expr = self.ast.copy(computed_expr);
|
||||
expr.object = self.ast.identifier_reference_expression(ident);
|
||||
if let Some(property_ident) = property_ident {
|
||||
expr.expression =
|
||||
self.ast.identifier_reference_expression(property_ident);
|
||||
}
|
||||
assign_target =
|
||||
self.ast.simple_assignment_target_member_expression(
|
||||
MemberExpression::ComputedMemberExpression(expr),
|
||||
);
|
||||
} else {
|
||||
let property_ident =
|
||||
self.maybe_generate_memoised(&computed_expr.expression);
|
||||
|
||||
// let right = self.ast.copy(&computed_expr.object);
|
||||
let mut expr = self.ast.copy(computed_expr);
|
||||
// let target = AssignmentTarget::SimpleAssignmentTarget(
|
||||
// self.ast.simple_assignment_target_identifier(ident.clone()),
|
||||
// );
|
||||
// expr.object =
|
||||
// self.ast.assignment_expression(span, op, target, right);
|
||||
if let Some(property_ident) = &property_ident {
|
||||
let left = AssignmentTarget::SimpleAssignmentTarget(
|
||||
self.ast.simple_assignment_target_identifier(
|
||||
property_ident.clone(),
|
||||
),
|
||||
);
|
||||
let right = self.ast.copy(&computed_expr.expression);
|
||||
expr.expression =
|
||||
self.ast.assignment_expression(span, op, left, right);
|
||||
}
|
||||
left_expr = self.ast.member_expression(
|
||||
MemberExpression::ComputedMemberExpression(expr),
|
||||
);
|
||||
|
||||
let mut expr = self.ast.copy(computed_expr);
|
||||
// expr.object = self.ast.identifier_reference_expression(ident);
|
||||
if let Some(property_ident) = property_ident {
|
||||
expr.expression =
|
||||
self.ast.identifier_reference_expression(property_ident);
|
||||
}
|
||||
assign_target =
|
||||
self.ast.simple_assignment_target_member_expression(
|
||||
MemberExpression::ComputedMemberExpression(expr),
|
||||
);
|
||||
};
|
||||
}
|
||||
MemberExpression::PrivateFieldExpression(_) => return,
|
||||
}
|
||||
}
|
||||
// All other are TypeScript syntax.
|
||||
_ => return,
|
||||
|
|
@ -52,15 +196,16 @@ impl<'a> LogicalAssignmentOperators<'a> {
|
|||
AssignmentTarget::AssignmentTargetPattern(_) => return,
|
||||
};
|
||||
|
||||
// Create the right hand side
|
||||
// a || (a = b)
|
||||
// ^^^^^^^
|
||||
let assign_op = AssignmentOperator::Assign;
|
||||
let right = self.ast.copy(&assignment_expr.right);
|
||||
let right = self.ast.assignment_expression(Span::default(), assign_op, left1, right);
|
||||
let right = self.ast.parenthesized_expression(Span::default(), right);
|
||||
let assign_target = AssignmentTarget::SimpleAssignmentTarget(assign_target);
|
||||
let right = self.ast.move_expression(&mut assignment_expr.right);
|
||||
let right =
|
||||
self.ast.assignment_expression(Span::default(), assign_op, assign_target, right);
|
||||
|
||||
let logical_expr = self.ast.logical_expression(Span::default(), left_expr, operator, right);
|
||||
|
||||
let logical_expr = self.ast.logical_expression(Span::default(), left2, operator, right);
|
||||
*expr = logical_expr;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test all permutations
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ impl<'a> Transformer<'a> {
|
|||
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),
|
||||
es2021_logical_assignment_operators: LogicalAssignmentOperators::new(Rc::clone(&ast), ctx.clone(), &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), ctx.clone(), &options),
|
||||
|
|
@ -100,8 +100,10 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
|
|||
for stmt in stmts.iter_mut() {
|
||||
self.visit_statement(stmt);
|
||||
}
|
||||
self.es2016_exponentiation_operator.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
||||
// TODO: we need scope id to insert the vars into the correct statements
|
||||
self.es2021_logical_assignment_operators.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
||||
self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
||||
self.es2016_exponentiation_operator.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
||||
}
|
||||
|
||||
fn visit_expression(&mut self, expr: &mut Expression<'a>) {
|
||||
|
|
|
|||
|
|
@ -2,57 +2,10 @@ use std::mem;
|
|||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{Atom, Span};
|
||||
use oxc_span::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 {
|
||||
let mut parts = std::vec::Vec::with_capacity(1);
|
||||
expr.gather(&mut |part| parts.push(part));
|
||||
let name = parts.join("$");
|
||||
Atom::from(format!("_{name}"))
|
||||
}
|
||||
|
||||
// TODO: <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L61>
|
||||
pub trait GatherNodeParts {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F);
|
||||
}
|
||||
|
||||
impl<'a> GatherNodeParts for Expression<'a> {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
match self {
|
||||
Self::Identifier(ident) => f(ident.name.clone()),
|
||||
Self::MemberExpression(expr) => expr.gather(f),
|
||||
_ => f(Atom::from("ref")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GatherNodeParts for MemberExpression<'a> {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
self.object().gather(f);
|
||||
match self {
|
||||
MemberExpression::ComputedMemberExpression(expr) => expr.expression.gather(f),
|
||||
MemberExpression::StaticMemberExpression(expr) => expr.property.gather(f),
|
||||
MemberExpression::PrivateFieldExpression(expr) => expr.field.gather(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GatherNodeParts for IdentifierName {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
f(self.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl GatherNodeParts for PrivateIdentifier {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
f(self.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateVars<'a> {
|
||||
fn ctx(&self) -> &TransformerCtx<'a>;
|
||||
|
||||
|
|
@ -71,17 +24,29 @@ pub trait CreateVars<'a> {
|
|||
stmts.insert(0, stmt);
|
||||
}
|
||||
|
||||
fn create_new_var(&mut self, expr: &Expression<'a>) -> Atom {
|
||||
let name = generate_uid_based_on_node(expr);
|
||||
// TODO: scope.push({ id: temp });
|
||||
fn create_new_var(&mut self, expr: &Expression<'a>) -> IdentifierReference {
|
||||
let name = self.ctx().scopes().generate_uid_based_on_node(expr);
|
||||
self.ctx().add_binding(name.clone());
|
||||
|
||||
// Add `var name` to scope
|
||||
// TODO: hookup symbol id
|
||||
let binding_identifier = BindingIdentifier::new(Span::default(), name.clone());
|
||||
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.ctx().ast.variable_declarator(Span::default(), kind, binding, None, false);
|
||||
self.vars_mut().push(decl);
|
||||
name
|
||||
// TODO: add reference id and flag
|
||||
IdentifierReference::new(Span::default(), name)
|
||||
}
|
||||
|
||||
/// Possibly generate a memoised identifier if it is not static and has consequences.
|
||||
/// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L578>
|
||||
fn maybe_generate_memoised(&mut self, expr: &Expression<'a>) -> Option<IdentifierReference> {
|
||||
if self.ctx().symbols().is_static(expr) {
|
||||
None
|
||||
} else {
|
||||
Some(self.create_new_var(expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Passed: 159/1091
|
||||
Passed: 161/1091
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-numeric-separator
|
||||
|
|
@ -484,10 +484,8 @@ Passed: 159/1091
|
|||
* Failed: to-native-fields/static-shadow/input.js
|
||||
* Failed: to-native-fields/static-shadowed-binding/input.js
|
||||
|
||||
# babel-plugin-transform-logical-assignment-operators (3/6)
|
||||
* Failed: logical-assignment/general-semantics/input.js
|
||||
# babel-plugin-transform-logical-assignment-operators (5/6)
|
||||
* Failed: logical-assignment/null-coalescing/input.js
|
||||
* Failed: logical-assignment/null-coalescing-without-other/input.js
|
||||
|
||||
# babel-plugin-transform-export-namespace-from (0/4)
|
||||
* Failed: export-namespace/namespace-default/input.mjs
|
||||
|
|
|
|||
Loading…
Reference in a new issue