From dfee8539f0ece6df77402f7560b7f07b9558c026 Mon Sep 17 00:00:00 2001 From: Boshen Date: Fri, 20 Oct 2023 16:27:23 +0800 Subject: [PATCH] feat(transformer): add utils to make logical_assignment_operators pass (#1017) --- .../gather_node_parts.rs | 82 ++++++++ .../src/syntax_directed_operations/mod.rs | 4 +- crates/oxc_codegen/src/gen.rs | 13 +- crates/oxc_semantic/src/scope.rs | 27 ++- crates/oxc_semantic/src/symbol.rs | 11 +- crates/oxc_transformer/src/context.rs | 23 ++- .../src/es2016/exponentiation_operator.rs | 5 +- .../src/es2020/nullish_coalescing_operator.rs | 6 +- .../es2021/logical_assignment_operators.rs | 183 ++++++++++++++++-- crates/oxc_transformer/src/lib.rs | 6 +- crates/oxc_transformer/src/utils.rs | 69 ++----- tasks/transform_conformance/babel.snap.md | 6 +- 12 files changed, 340 insertions(+), 95 deletions(-) create mode 100644 crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs diff --git a/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs b/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs new file mode 100644 index 0000000000000..a727f8d22532b --- /dev/null +++ b/crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs @@ -0,0 +1,82 @@ +use crate::ast::*; +use oxc_span::Atom; + +// TODO: +pub trait GatherNodeParts { + fn gather(&self, f: &mut F); +} + +impl<'a> GatherNodeParts for Expression<'a> { + fn gather(&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(&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(&self, f: &mut F) { + match self { + AssignmentTarget::SimpleAssignmentTarget(t) => t.gather(f), + AssignmentTarget::AssignmentTargetPattern(_) => {} + } + } +} + +impl<'a> GatherNodeParts for SimpleAssignmentTarget<'a> { + fn gather(&self, f: &mut F) { + match self { + Self::AssignmentTargetIdentifier(ident) => ident.gather(f), + Self::MemberAssignmentTarget(expr) => expr.gather(f), + _ => {} + } + } +} + +impl GatherNodeParts for IdentifierReference { + fn gather(&self, f: &mut F) { + f(self.name.clone()); + } +} + +impl GatherNodeParts for IdentifierName { + fn gather(&self, f: &mut F) { + f(self.name.clone()); + } +} + +impl GatherNodeParts for PrivateIdentifier { + fn gather(&self, f: &mut F) { + f(self.name.clone()); + } +} + +impl GatherNodeParts for StringLiteral { + fn gather(&self, f: &mut F) { + f(self.value.clone()); + } +} diff --git a/crates/oxc_ast/src/syntax_directed_operations/mod.rs b/crates/oxc_ast/src/syntax_directed_operations/mod.rs index f44cc8bd4958a..a70cce9f0f74f 100644 --- a/crates/oxc_ast/src/syntax_directed_operations/mod.rs +++ b/crates/oxc_ast/src/syntax_directed_operations/mod.rs @@ -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, }; diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index b514604dbf5cd..e561f4266363f 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1182,9 +1182,12 @@ impl<'a, const MINIFY: bool> Gen for ArrayExpression<'a> { impl<'a, const MINIFY: bool> GenExpr 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 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'}'); }); } diff --git a/crates/oxc_semantic/src/scope.rs b/crates/oxc_semantic/src/scope.rs index 10c6c47419d98..a5048b1350b43 100644 --- a/crates/oxc_semantic/src/scope.rs +++ b/crates/oxc_semantic/src/scope.rs @@ -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 { 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: + // + 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}") }) + } } diff --git a/crates/oxc_semantic/src/symbol.rs b/crates/oxc_semantic/src/symbol.rs index 41317096ec454..397c529da10fe 100644 --- a/crates/oxc_semantic/src/symbol.rs +++ b/crates/oxc_semantic/src/symbol.rs @@ -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, } diff --git a/crates/oxc_transformer/src/context.rs b/crates/oxc_transformer/src/context.rs index f30349d85bf8d..84c0e8160373e 100644 --- a/crates/oxc_transformer/src/context.rs +++ b/crates/oxc_transformer/src/context.rs @@ -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>, pub scopes: Rc>, } + +impl<'a> TransformerCtx<'a> { + pub fn symbols(&self) -> Ref { + self.symbols.borrow() + } + + pub fn scopes(&self) -> Ref { + 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)); + } +} diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs index 02dcbe2bbce29..a13510d8cf56c 100644 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs @@ -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); diff --git a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs index 8ad463efc453f..555400b270965 100644 --- a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs +++ b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs @@ -30,6 +30,7 @@ pub struct NullishCoalescingOperator<'a> { ast: Rc>, 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), diff --git a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs index ea074f7eb66f1..5c5cc736a77a7 100644 --- a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs +++ b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs @@ -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}; /// * pub struct LogicalAssignmentOperators<'a> { ast: Rc>, + 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>, options: &TransformOptions) -> Option { - (options.target < TransformTarget::ES2021 || options.logical_assignment_operators) - .then(|| Self { ast }) + pub fn new( + ast: Rc>, + ctx: TransformerCtx<'a>, + options: &TransformOptions, + ) -> Option { + (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 diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 8eb2c00a304c8..9619e641f5500 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -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>) { diff --git a/crates/oxc_transformer/src/utils.rs b/crates/oxc_transformer/src/utils.rs index 2016b8938af7a..2489779a695c3 100644 --- a/crates/oxc_transformer/src/utils.rs +++ b/crates/oxc_transformer/src/utils.rs @@ -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: -// -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: -pub trait GatherNodeParts { - fn gather(&self, f: &mut F); -} - -impl<'a> GatherNodeParts for Expression<'a> { - fn gather(&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(&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(&self, f: &mut F) { - f(self.name.clone()); - } -} - -impl GatherNodeParts for PrivateIdentifier { - fn gather(&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. + /// + fn maybe_generate_memoised(&mut self, expr: &Expression<'a>) -> Option { + if self.ctx().symbols().is_static(expr) { + None + } else { + Some(self.create_new_var(expr)) + } } } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 9fa696c1362be..1124ee10dd19a 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -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