Skip to content

Commit

Permalink
refactor(transformer/class-properties): PrivatePropsStack type
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Dec 2, 2024
1 parent cafb613 commit 3547117
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 60 deletions.
3 changes: 2 additions & 1 deletion crates/oxc_transformer/src/es2022/class_properties/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ use crate::common::helper_loader::Helper;

use super::super::ClassStaticBlock;
use super::{
private_props::{PrivateProp, PrivateProps},
utils::{
create_assignment, create_underscore_ident_name, create_variable_declaration,
exprs_into_stmts,
},
ClassName, ClassProperties, FxIndexMap, PrivateProp, PrivateProps,
ClassName, ClassProperties, FxIndexMap,
};

impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
Expand Down
26 changes: 6 additions & 20 deletions crates/oxc_transformer/src/es2022/class_properties/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
//! * `class.rs`: Transform of class body.
//! * `constructor.rs`: Insertion of property initializers into class constructor.
//! * `private.rs`: Transform of private property usages (`this.#prop`).
//! * `private_props.rs`: Structures storing details of private properties.
//! * `static_prop.rs`: Transform of static property initializers.
//! * `utils.rs`: Utility functions.
//!
Expand All @@ -146,16 +147,18 @@ use serde::Deserialize;

use oxc_allocator::{Address, GetAddress};
use oxc_ast::ast::*;
use oxc_data_structures::stack::{NonEmptyStack, SparseStack};
use oxc_data_structures::stack::NonEmptyStack;
use oxc_traverse::{BoundIdentifier, Traverse, TraverseCtx};

use crate::TransformCtx;

mod class;
mod constructor;
mod private;
mod private_props;
mod static_prop;
mod utils;
use private_props::PrivatePropsStack;

type FxIndexMap<K, V> = IndexMap<K, V, FxBuildHasher>;

Expand Down Expand Up @@ -192,7 +195,7 @@ pub struct ClassProperties<'a, 'ctx> {
// then stack will get out of sync.
// TODO: Should push to the stack only when entering class body, because `#x` in class `extends`
// clause resolves to `#x` in *outer* class, not the current class.
private_props_stack: SparseStack<PrivateProps<'a>>,
private_props_stack: PrivatePropsStack<'a>,
/// Addresses of class expressions being processed, to prevent same class being visited twice.
/// Have to use a stack because the revisit doesn't necessarily happen straight after the first visit.
/// e.g. `c = class C { [class D {}] = 1; }` -> `c = (_D = class D {}, class C { ... })`
Expand Down Expand Up @@ -223,23 +226,6 @@ enum ClassName<'a> {
Name(&'a str),
}

/// Details of private properties for a class.
struct PrivateProps<'a> {
/// Private properties for class. Indexed by property name.
// TODO(improve-on-babel): Order that temp vars are created in is not important. Use `FxHashMap` instead.
props: FxIndexMap<Atom<'a>, PrivateProp<'a>>,
/// Binding for class, if class has name
class_binding: Option<BoundIdentifier<'a>>,
/// `true` for class declaration, `false` for class expression
is_declaration: bool,
}

/// Details of a private property.
struct PrivateProp<'a> {
binding: BoundIdentifier<'a>,
is_static: bool,
}

impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
pub fn new(
options: ClassPropertiesOptions,
Expand All @@ -254,7 +240,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
set_public_class_fields,
transform_static_blocks,
ctx,
private_props_stack: SparseStack::new(),
private_props_stack: PrivatePropsStack::default(),
class_expression_addresses_stack: NonEmptyStack::new(Address::DUMMY),
// Temporary values - overwritten when entering class
is_declaration: false,
Expand Down
44 changes: 5 additions & 39 deletions crates/oxc_transformer/src/es2022/class_properties/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,11 @@ use oxc_traverse::{ast_operations::get_var_name_from_node, BoundIdentifier, Trav
use crate::common::helper_loader::Helper;

use super::{
private_props::ResolvedPrivateProp,
utils::{create_assignment, create_underscore_ident_name},
ClassProperties,
};

/// Details of a private property resolved for a private field.
///
/// This is the return value of `lookup_private_property`.
struct ResolvedPrivateProp<'a, 'b> {
/// Binding for temp var representing the property
prop_binding: &'b BoundIdentifier<'a>,
/// Binding for class (if it has one)
class_binding: Option<&'b BoundIdentifier<'a>>,
/// `true` if is a static property
is_static: bool,
/// `true` if class which defines this property is a class declaration
is_declaration: bool,
}

impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
/// Transform private field expression.
///
Expand All @@ -56,7 +43,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
field_expr: ArenaBox<'a, PrivateFieldExpression<'a>>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let prop_details = self.lookup_private_property(&field_expr.field);
let prop_details = self.private_props_stack.find(&field_expr.field);
// TODO: Should never be `None` - only because implementation is incomplete.
let Some(prop_details) = prop_details else {
return Expression::PrivateFieldExpression(field_expr);
Expand Down Expand Up @@ -199,7 +186,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
) -> Option<(Expression<'a>, Expression<'a>)> {
// TODO: Should never be `None` - only because implementation is incomplete.
let ResolvedPrivateProp { prop_binding, class_binding, is_static, is_declaration } =
self.lookup_private_property(&field_expr.field)?;
self.private_props_stack.find(&field_expr.field)?;
let prop_ident = prop_binding.create_read_expression(ctx);

let object = ctx.ast.move_expression(&mut field_expr.object);
Expand Down Expand Up @@ -278,7 +265,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
unreachable!()
};

let prop_details = self.lookup_private_property(&field_expr.field);
let prop_details = self.private_props_stack.find(&field_expr.field);
// TODO: Should never be `None` - only because implementation is incomplete.
let Some(prop_details) = prop_details else { return };
let ResolvedPrivateProp { prop_binding, class_binding, is_static, is_declaration } =
Expand Down Expand Up @@ -629,7 +616,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
_ => unreachable!(),
};

let prop_details = self.lookup_private_property(&field_expr.field);
let prop_details = self.private_props_stack.find(&field_expr.field);
// TODO: Should never be `None` - only because implementation is incomplete.
let Some(prop_details) = prop_details else { return };
let ResolvedPrivateProp { prop_binding, class_binding, is_static, is_declaration } =
Expand Down Expand Up @@ -1052,27 +1039,6 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
Self::create_underscore_member_expression(func_call, span, ctx)
}

/// Lookup details of private property referred to by `ident`.
fn lookup_private_property<'b>(
&'b self,
ident: &PrivateIdentifier<'a>,
) -> Option<ResolvedPrivateProp<'a, 'b>> {
// Check for binding in closest class first, then enclosing classes
// TODO: Check there are tests for bindings in enclosing classes.
for private_props in self.private_props_stack.as_slice().iter().rev() {
if let Some(prop) = private_props.props.get(&ident.name) {
return Some(ResolvedPrivateProp {
prop_binding: &prop.binding,
class_binding: private_props.class_binding.as_ref(),
is_static: prop.is_static,
is_declaration: private_props.is_declaration,
});
}
}
// TODO: This should be unreachable. Only returning `None` because implementation is incomplete.
None
}

/// Create `<object>._` assignment target.
fn create_underscore_member_expr_target(
object: Expression<'a>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use oxc_ast::ast::PrivateIdentifier;
use oxc_data_structures::stack::SparseStack;
use oxc_span::Atom;
use oxc_traverse::BoundIdentifier;

use super::FxIndexMap;

/// Stack of private props defined by classes.
///
/// Pushed to when entering a class (`None` if class has no private props, `Some` if it does).
/// Entries contain a mapping from private prop name to binding for temp var for that property,
/// and details of the class that defines the private prop.
///
/// This is used as lookup when transforming e.g. `this.#x`.
#[derive(Default)]
pub(super) struct PrivatePropsStack<'a> {
stack: SparseStack<PrivateProps<'a>>,
}

impl<'a> PrivatePropsStack<'a> {
// Forward methods to underlying `SparseStack`

#[inline]
pub fn push(&mut self, props: Option<PrivateProps<'a>>) {
self.stack.push(props);
}

#[inline]
pub fn pop(&mut self) -> Option<PrivateProps<'a>> {
self.stack.pop()
}

#[inline]
pub fn last(&self) -> Option<&PrivateProps<'a>> {
self.stack.last()
}

#[inline]
#[expect(dead_code)]
pub fn last_mut(&mut self) -> Option<&mut PrivateProps<'a>> {
self.stack.last_mut()
}

/// Lookup details of private property referred to by `ident`.
pub fn find<'b>(
&'b self,
ident: &PrivateIdentifier<'a>,
) -> Option<ResolvedPrivateProp<'a, 'b>> {
// Check for binding in closest class first, then enclosing classes
// TODO: Check there are tests for bindings in enclosing classes.
for private_props in self.stack.as_slice().iter().rev() {
if let Some(prop) = private_props.props.get(&ident.name) {
return Some(ResolvedPrivateProp {
prop_binding: &prop.binding,
class_binding: private_props.class_binding.as_ref(),
is_static: prop.is_static,
is_declaration: private_props.is_declaration,
});
}
}
// TODO: This should be unreachable. Only returning `None` because implementation is incomplete.
None
}
}

/// Details of private properties for a class.
pub(super) struct PrivateProps<'a> {
/// Private properties for class. Indexed by property name.
// TODO(improve-on-babel): Order that temp vars are created in is not important. Use `FxHashMap` instead.
pub props: FxIndexMap<Atom<'a>, PrivateProp<'a>>,
/// Binding for class, if class has name
pub class_binding: Option<BoundIdentifier<'a>>,
/// `true` for class declaration, `false` for class expression
pub is_declaration: bool,
}

/// Details of a private property.
pub(super) struct PrivateProp<'a> {
pub binding: BoundIdentifier<'a>,
pub is_static: bool,
}

/// Details of a private property resolved for a private field.
///
/// This is the return value of [`PrivatePropsStack::find`].
pub(super) struct ResolvedPrivateProp<'a, 'b> {
/// Binding for temp var representing the property
pub prop_binding: &'b BoundIdentifier<'a>,
/// Binding for class (if it has one)
pub class_binding: Option<&'b BoundIdentifier<'a>>,
/// `true` if is a static property
pub is_static: bool,
/// `true` if class which defines this property is a class declaration
pub is_declaration: bool,
}

0 comments on commit 3547117

Please sign in to comment.