Skip to content

Commit

Permalink
feat(transformer/class-properties): transform static/instance accesso…
Browse files Browse the repository at this point in the history
…r methods
  • Loading branch information
Dunqing committed Dec 26, 2024
1 parent 727cba8 commit 0f5cc4d
Show file tree
Hide file tree
Showing 13 changed files with 399 additions and 186 deletions.
35 changes: 24 additions & 11 deletions crates/oxc_transformer/src/es2022/class_properties/class.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! ES2022: Class Properties
//! Transform of class itself.
use indexmap::map::Entry;
use oxc_allocator::{Address, GetAddress};
use oxc_ast::{ast::*, NONE};
use oxc_span::SPAN;
Expand Down Expand Up @@ -93,7 +94,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
let binding = ctx.generate_uid_in_current_hoist_scope(&ident.name);
private_props.insert(
ident.name.clone(),
PrivateProp::new(binding, prop.r#static, false, false),
PrivateProp::new(binding, prop.r#static, None, false),
);
}

Expand Down Expand Up @@ -135,10 +136,22 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
ctx.current_block_scope_id(),
SymbolFlags::FunctionScopedVariable,
);
private_props.insert(
ident.name.clone(),
PrivateProp::new(binding, method.r#static, true, false),
);

match private_props.entry(ident.name.clone()) {
Entry::Occupied(mut entry) => {
// If there's already a binding for this private property,
// it's a setter or getter, so store the binding in `binding2`.
entry.get_mut().set_binding2(binding);
}
Entry::Vacant(entry) => {
entry.insert(PrivateProp::new(
binding,
method.r#static,
Some(method.kind),
false,
));
}
}
}
}
ClassElement::AccessorProperty(prop) => {
Expand All @@ -148,7 +161,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
let dummy_binding = BoundIdentifier::new(Atom::empty(), SymbolId::new(0));
private_props.insert(
ident.name.clone(),
PrivateProp::new(dummy_binding, prop.r#static, true, true),
PrivateProp::new(dummy_binding, prop.r#static, None, true),
);
}
}
Expand Down Expand Up @@ -443,7 +456,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
self.ctx.statement_injector.insert_many_before(
&stmt_address,
private_props.iter().filter_map(|(name, prop)| {
if prop.is_method || prop.is_accessor {
if prop.is_method() || prop.is_accessor {
return None;
}

Expand All @@ -459,10 +472,10 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
self.ctx.statement_injector.insert_many_before(
&stmt_address,
private_props.values().filter_map(|prop| {
if prop.is_static || (prop.is_method && has_method) || prop.is_accessor {
if prop.is_static || (prop.is_method() && has_method) || prop.is_accessor {
return None;
}
if prop.is_method {
if prop.is_method() {
// `var _C_brand = new WeakSet();`
has_method = true;
let binding = class_details.bindings.brand();
Expand Down Expand Up @@ -583,7 +596,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
// TODO(improve-on-babel): Simplify this.
if self.private_fields_as_properties {
exprs.extend(private_props.iter().filter_map(|(name, prop)| {
if prop.is_method || prop.is_accessor {
if prop.is_method() || prop.is_accessor {
return None;
}

Expand All @@ -598,7 +611,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
let mut weakmap_symbol_id = None;
let mut has_method = false;
exprs.extend(private_props.values().filter_map(|prop| {
if prop.is_method || prop.is_accessor {
if prop.is_method() || prop.is_accessor {
if prop.is_static || has_method {
return None;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,29 @@ impl<'a> ClassDetails<'a> {
pub(super) struct PrivateProp<'a> {
pub binding: BoundIdentifier<'a>,
pub is_static: bool,
pub is_method: bool,
pub method_kind: Option<MethodDefinitionKind>,
pub is_accessor: bool,
// For accessor methods, they have two bindings,
// one for getter and one for setter.
pub binding2: Option<BoundIdentifier<'a>>,
}

impl<'a> PrivateProp<'a> {
pub fn new(
binding: BoundIdentifier<'a>,
is_static: bool,
is_method: bool,
method_kind: Option<MethodDefinitionKind>,
is_accessor: bool,
) -> Self {
Self { binding, is_static, is_method, is_accessor }
Self { binding, is_static, method_kind, is_accessor, binding2: None }
}

pub fn is_method(&self) -> bool {
self.method_kind.is_some()
}

pub fn set_binding2(&mut self, binding: BoundIdentifier<'a>) {
self.binding2 = Some(binding);
}
}

Expand Down Expand Up @@ -105,19 +116,34 @@ impl<'a> ClassesStack<'a> {
pub fn find_private_prop<'b>(
&'b mut self,
ident: &PrivateIdentifier<'a>,
is_assignment: bool,
) -> ResolvedPrivateProp<'a, 'b> {
// Check for binding in closest class first, then enclosing classes.
// We skip the first, because this is a `NonEmptyStack` with dummy first entry.
// TODO: Check there are tests for bindings in enclosing classes.
for class in self.stack[1..].iter_mut().rev() {
if let Some(private_props) = &mut class.private_props {
if let Some(prop) = private_props.get(&ident.name) {
let prop_binding = match (is_assignment, prop.method_kind) {
(false, Some(MethodDefinitionKind::Set))
| (true, Some(MethodDefinitionKind::Get)) => {
if let Some(binding2) = &prop.binding2 {
binding2
} else {
// TODO: Warn there is no setter method
&prop.binding
}
}
_ => &prop.binding,
};

return ResolvedPrivateProp {
prop_binding: &prop.binding,
prop_binding,
class_bindings: &mut class.bindings,
is_static: prop.is_static,
is_method: prop.is_method,
is_accessor: prop.is_accessor,
is_method: prop.method_kind.is_some(),
is_accessor: prop.is_accessor
|| prop.method_kind.is_some_and(|kind| kind.is_accessor()),
is_declaration: class.is_declaration,
};
}
Expand All @@ -140,7 +166,8 @@ pub(super) struct ResolvedPrivateProp<'a, 'b> {
pub is_static: bool,
/// `true` if is a private method
pub is_method: bool,
/// `true` if is a private accessor property
/// `true` if is a private accessor property or [`PrivateProp::method_kind`] is
/// `Some(MethodDefinitionKind::Get)` or `Some(MethodDefinitionKind::Set)`
pub is_accessor: bool,
/// `true` if class which defines this property is a class declaration
pub is_declaration: bool,
Expand Down
Loading

0 comments on commit 0f5cc4d

Please sign in to comment.