Skip to content

Commit bf6fa4d

Browse files
committed
feat(transformer/class-properties): transform static private method invoking
1 parent db44b5d commit bf6fa4d

File tree

4 files changed

+234
-275
lines changed

4 files changed

+234
-275
lines changed

crates/oxc_transformer/src/es2022/class_properties/class.rs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
7676

7777
// Check if class has any properties or statick blocks, and locate constructor (if class has one)
7878
let mut instance_prop_count = 0;
79+
let mut has_instance_private_method = false;
7980
let mut has_static_prop = false;
80-
let mut has_private_method = false;
8181
let mut has_static_block = false;
8282
// TODO: Store `FxIndexMap`s in a pool and re-use them
8383
let mut private_props = FxIndexMap::default();
@@ -118,7 +118,12 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
118118
constructor = Some(method);
119119
}
120120
} else if let PropertyKey::PrivateIdentifier(ident) = &method.key {
121-
has_private_method = true;
121+
if method.r#static {
122+
has_static_prop = true;
123+
} else {
124+
has_instance_private_method = true;
125+
}
126+
122127
let name = match method.kind {
123128
MethodDefinitionKind::Method => ident.name.as_str(),
124129
MethodDefinitionKind::Get => &format!("get_{}", ident.name),
@@ -154,7 +159,10 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
154159
}
155160

156161
// Exit if nothing to transform
157-
if instance_prop_count == 0 && !has_static_prop && !has_static_block && !has_private_method
162+
if instance_prop_count == 0
163+
&& !has_static_prop
164+
&& !has_static_block
165+
&& !has_instance_private_method
158166
{
159167
self.classes_stack.push(ClassDetails {
160168
is_declaration,
@@ -193,7 +201,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
193201
None
194202
};
195203

196-
let class_brand_binding = has_private_method.then(|| {
204+
let class_brand_binding = has_instance_private_method.then(|| {
197205
// `_Class_brand`
198206
let name = class_name_binding.as_ref().map_or_else(|| "Class", |binding| &binding.name);
199207
let name = &format!("_{name}_brand");
@@ -219,7 +227,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
219227
});
220228

221229
// Exit if no instance properties (public or private)
222-
if instance_prop_count == 0 && !has_private_method {
230+
if instance_prop_count == 0 && !has_instance_private_method {
223231
return;
224232
}
225233

@@ -271,10 +279,10 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
271279
// Those assignments will be moved to before class in exit phase of the transform.
272280
// -> `_foo = foo(); class C {}`
273281
let mut instance_inits =
274-
Vec::with_capacity(instance_prop_count + usize::from(has_private_method));
282+
Vec::with_capacity(instance_prop_count + usize::from(has_instance_private_method));
275283

276284
// `_classPrivateMethodInitSpec(this, _C_brand);`
277-
if has_private_method {
285+
if has_instance_private_method {
278286
instance_inits.push(self.create_class_private_method_init_spec(ctx));
279287
}
280288

@@ -590,11 +598,10 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
590598
let mut weakmap_symbol_id = None;
591599
let mut has_method = false;
592600
exprs.extend(private_props.values().filter_map(|prop| {
593-
if (prop.is_method && has_method) || prop.is_accessor {
594-
return None;
595-
}
596-
597-
if prop.is_method {
601+
if prop.is_method || prop.is_accessor {
602+
if prop.is_static || has_method {
603+
return None;
604+
}
598605
has_method = true;
599606
// `_C_brand = new WeakSet()`
600607
let binding = class_details.bindings.brand();
@@ -645,6 +652,14 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
645652
// `_Class = class {}`
646653
let class_expr = ctx.ast.move_expression(expr);
647654
let assignment = create_assignment(binding, class_expr, ctx);
655+
656+
if exprs.is_empty() && self.insert_after_exprs.is_empty() {
657+
// No need to wrap in sequence if no static property
658+
// and static blocks
659+
*expr = assignment;
660+
return;
661+
}
662+
648663
exprs.push(assignment);
649664
// Add static property assignments + static blocks
650665
exprs.extend(self.insert_after_exprs.drain(..));

crates/oxc_transformer/src/es2022/class_properties/private_field.rs

Lines changed: 58 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,6 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
6161
} = self.classes_stack.find_private_prop(&field_expr.field);
6262

6363
let span = field_expr.span;
64-
65-
if is_method || is_accessor {
66-
let prop_ident = prop_binding.create_read_expression(ctx);
67-
return if is_assignment {
68-
// TODO: Handle assignment to private method or accessor
69-
None
70-
} else {
71-
Some(self.create_assert_class_brand_for_private_method(prop_ident, span, ctx))
72-
};
73-
};
74-
7564
let object = ctx.ast.move_expression(&mut field_expr.object);
7665

7766
if self.private_fields_as_properties {
@@ -88,6 +77,11 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
8877
let prop_ident = prop_binding.create_read_expression(ctx);
8978

9079
let replacement = if is_static {
80+
if is_assignment && (is_method || is_accessor) {
81+
// TODO: Handle assignment to static private method or static accessor
82+
return None;
83+
}
84+
9185
// TODO: Ensure there are tests for nested classes with references to private static props
9286
// of outer class inside inner class, to make sure we're getting the right `class_bindings`.
9387

@@ -100,23 +94,37 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
10094
) {
10195
// `_prop._`
10296
ctx.symbols_mut().delete_resolved_reference(class_symbol_id, object_reference_id);
103-
Self::create_underscore_member_expression(prop_ident, span, ctx)
97+
if is_method || is_accessor {
98+
prop_ident
99+
} else {
100+
Self::create_underscore_member_expression(prop_ident, span, ctx)
101+
}
104102
} else {
105103
// `_assertClassBrand(Class, object, _prop)._`
106104
let class_binding = class_bindings.get_or_init_static_binding(ctx);
107105
let class_ident = class_binding.create_read_expression(ctx);
108-
109-
self.create_assert_class_brand_underscore(
110-
class_ident,
111-
object,
112-
prop_ident,
113-
span,
114-
ctx,
115-
)
106+
if is_method || is_accessor {
107+
self.create_assert_class_brand(class_ident, object, prop_ident, span, ctx)
108+
} else {
109+
self.create_assert_class_brand_underscore(
110+
class_ident,
111+
object,
112+
prop_ident,
113+
span,
114+
ctx,
115+
)
116+
}
116117
}
117118
} else if is_assignment {
119+
if is_method || is_accessor {
120+
// TODO: Handle assignment to private method or accessor
121+
return None;
122+
}
118123
// `_toSetter(_classPrivateFieldSet2, [_prop, object])._`
119124
self.create_to_setter(prop_ident, object, span, ctx)
125+
} else if is_method || is_accessor {
126+
let brand_ident = class_bindings.brand().create_read_expression(ctx);
127+
self.create_assert_class_brand(brand_ident, object, prop_ident, span, ctx)
120128
} else {
121129
// `_classPrivateFieldGet2(_prop, object)`
122130
self.create_private_field_get(prop_ident, object, span, ctx)
@@ -278,13 +286,6 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
278286
let span = field_expr.span;
279287
let prop_ident = prop_binding.create_read_expression(ctx);
280288

281-
if is_method || is_accessor {
282-
return (
283-
self.create_assert_class_brand_for_private_method(prop_ident, span, ctx),
284-
ctx.ast.expression_this(SPAN),
285-
);
286-
};
287-
288289
// `(object.#method)()`
289290
// ^^^^^^^^^^^^^^^^ is a parenthesized expression
290291
let object = ctx.ast.move_expression(field_expr.object.get_inner_expression_mut());
@@ -308,8 +309,13 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
308309
)
309310
.is_some()
310311
{
311-
// `_prop._`
312-
let callee = Self::create_underscore_member_expression(prop_ident, span, ctx);
312+
let callee = if is_method || is_accessor {
313+
// `_prop`
314+
prop_ident
315+
} else {
316+
// `_prop._`
317+
Self::create_underscore_member_expression(prop_ident, span, ctx)
318+
};
313319
(callee, object)
314320
} else {
315321
let class_binding = class_bindings.get_or_init_static_binding(ctx);
@@ -318,24 +324,36 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
318324
// Make 2 copies of `object`
319325
let (object1, object2) = self.duplicate_object(object, ctx);
320326

321-
// `_assertClassBrand(Class, object, _prop)._`
322-
let assert_obj = self.create_assert_class_brand_underscore(
323-
class_ident,
324-
object1,
325-
prop_ident,
326-
span,
327-
ctx,
328-
);
327+
let assert_obj = if is_method || is_accessor {
328+
// `_assertClassBrand(Class, object, _prop)._`
329+
self.create_assert_class_brand(class_ident, object1, prop_ident, span, ctx)
330+
} else {
331+
// `_assertClassBrand(Class, object, _prop)._`
332+
self.create_assert_class_brand_underscore(
333+
class_ident,
334+
object1,
335+
prop_ident,
336+
span,
337+
ctx,
338+
)
339+
};
340+
329341
(assert_obj, object2)
330342
}
331343
} else {
332344
// `object.#prop(arg)` -> `_classPrivateFieldGet2(_prop, object).call(object, arg)`
333345
// Make 2 copies of `object`
334346
let (object1, object2) = self.duplicate_object(object, ctx);
335347

336-
// `_classPrivateFieldGet2(_prop, object)`
337-
let get_call = self.create_private_field_get(prop_ident, object1, span, ctx);
338-
(get_call, object2)
348+
let callee = if is_method || is_accessor {
349+
// `(_Class_brand, this)`
350+
let brand_ident = self.current_class().bindings.brand().create_read_expression(ctx);
351+
self.create_assert_class_brand(brand_ident, object1, prop_ident, span, ctx)
352+
} else {
353+
// `_classPrivateFieldGet2(_prop, object)`
354+
self.create_private_field_get(prop_ident, object1, span, ctx)
355+
};
356+
(callee, object2)
339357
};
340358

341359
replacement
@@ -1795,19 +1813,6 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
17951813
)
17961814
}
17971815

1798-
/// `_assertClassBrand(_Class_brand, object, _prop)`
1799-
#[inline]
1800-
fn create_assert_class_brand_for_private_method(
1801-
&self,
1802-
value_or_prop_ident: Expression<'a>,
1803-
span: Span,
1804-
ctx: &mut TraverseCtx<'a>,
1805-
) -> Expression<'a> {
1806-
let class_ident = self.current_class().bindings.brand().create_read_expression(ctx);
1807-
let object = ctx.ast.expression_this(SPAN);
1808-
self.create_assert_class_brand(class_ident, object, value_or_prop_ident, span, ctx)
1809-
}
1810-
18111816
/// `_assertClassBrand(Class, object, _prop)._`
18121817
fn create_assert_class_brand_underscore(
18131818
&self,

tasks/transform_conformance/snapshots/babel.snap.md

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
commit: 54a8389f
22

3-
Passed: 632/1095
3+
Passed: 641/1095
44

55
# All Passed:
66
* babel-plugin-transform-logical-assignment-operators
@@ -358,7 +358,12 @@ x Output mismatch
358358
x Output mismatch
359359

360360
* private/static-self-method/input.js
361-
x Output mismatch
361+
Scope flags mismatch:
362+
after transform: ScopeId(4): ScopeFlags(StrictMode | Function)
363+
rebuilt : ScopeId(4): ScopeFlags(Function)
364+
Scope flags mismatch:
365+
after transform: ScopeId(6): ScopeFlags(StrictMode | Function)
366+
rebuilt : ScopeId(6): ScopeFlags(Function)
362367

363368
* private/static-shadow/input.js
364369
x Output mismatch
@@ -462,7 +467,7 @@ x Output mismatch
462467
x Output mismatch
463468

464469

465-
# babel-plugin-transform-private-methods (17/148)
470+
# babel-plugin-transform-private-methods (26/148)
466471
* accessors/arguments/input.js
467472
x Output mismatch
468473

@@ -675,36 +680,9 @@ x Output mismatch
675680
`----
676681

677682

678-
* private-static-method/basic/input.js
679-
x Output mismatch
680-
681-
* private-static-method/class-check/input.js
682-
x Output mismatch
683-
684-
* private-static-method/class-expression/input.js
685-
x Output mismatch
686-
687-
* private-static-method/exfiltrated/input.js
688-
x Output mismatch
689-
690-
* private-static-method/generator/input.js
691-
x Output mismatch
692-
693-
* private-static-method/preserve-comments/input.js
694-
x Output mismatch
695-
696683
* private-static-method/read-only/input.js
697684
x Output mismatch
698685

699-
* private-static-method/super/input.js
700-
x Output mismatch
701-
702-
* private-static-method/tagged-template/input.js
703-
x Output mismatch
704-
705-
* private-static-method/this/input.js
706-
x Output mismatch
707-
708686
* private-static-method-loose/async/input.js
709687

710688
x TS(1108): A 'return' statement can only be used within a function body.

0 commit comments

Comments
 (0)