Skip to content

Commit

Permalink
feat(transformer): add utils to make logical_assignment_operators pass (
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen authored Oct 20, 2023
1 parent 3f06335 commit dfee853
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 95 deletions.
82 changes: 82 additions & 0 deletions crates/oxc_ast/src/syntax_directed_operations/gather_node_parts.rs
Original file line number Diff line number Diff line change
@@ -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());
}
}
4 changes: 3 additions & 1 deletion crates/oxc_ast/src/syntax_directed_operations/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
};
13 changes: 9 additions & 4 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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'}');
});
}
Expand Down
27 changes: 26 additions & 1 deletion crates/oxc_semantic/src/scope.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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()
}
Expand All @@ -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);
}

Expand Down Expand Up @@ -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}") })
}
}
11 changes: 7 additions & 4 deletions crates/oxc_semantic/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
23 changes: 21 additions & 2 deletions crates/oxc_transformer/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
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> {
pub ast: Rc<AstBuilder<'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));
}
}
5 changes: 2 additions & 3 deletions crates/oxc_transformer/src/es2016/exponentiation_operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct NullishCoalescingOperator<'a> {

ast: Rc<AstBuilder<'a>>,
ctx: TransformerCtx<'a>,

vars: Vec<'a, VariableDeclarator<'a>>,
}

Expand Down Expand Up @@ -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),
Expand Down
Loading

0 comments on commit dfee853

Please sign in to comment.