Skip to content

Commit 1563017

Browse files
committed
feat(transformer/class-properties): transform private in expression
1 parent f6ea574 commit 1563017

File tree

10 files changed

+150
-109
lines changed

10 files changed

+150
-109
lines changed

crates/oxc_transformer/src/common/helper_loader.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ pub enum Helper {
163163
SuperPropSet,
164164
ReadOnlyError,
165165
WriteOnlyError,
166+
CheckInRHS,
166167
}
167168

168169
impl Helper {
@@ -191,6 +192,7 @@ impl Helper {
191192
Self::SuperPropSet => "superPropSet",
192193
Self::ReadOnlyError => "readOnlyError",
193194
Self::WriteOnlyError => "writeOnlyError",
195+
Self::CheckInRHS => "checkInRHS",
194196
}
195197
}
196198
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ impl<'a, 'ctx> Traverse<'a> for ClassProperties<'a, 'ctx> {
368368
Expression::TaggedTemplateExpression(_) => {
369369
self.transform_tagged_template_expression(expr, ctx);
370370
}
371+
// "#prop in object"
372+
Expression::PrivateInExpression(_) => {
373+
self.transform_private_in_expression(expr, ctx);
374+
}
371375
_ => {}
372376
}
373377
}

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

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use std::mem;
55

6-
use oxc_allocator::String as ArenaString;
6+
use oxc_allocator::{Box as ArenaBox, String as ArenaString};
77
use oxc_ast::{ast::*, NONE};
88
use oxc_span::SPAN;
99
use oxc_syntax::{reference::ReferenceId, symbol::SymbolId};
@@ -1820,6 +1820,63 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
18201820
*target = AssignmentTarget::from(replacement.into_member_expression());
18211821
}
18221822

1823+
/// Transform private field in expression.
1824+
///
1825+
/// * Static
1826+
/// `#prop in object` -> `_checkInRHS(object) === Class`
1827+
///
1828+
/// * Instance prop
1829+
/// `#prop in object` -> `_prop.has(_checkInRHS(object))`
1830+
///
1831+
/// * Instance method
1832+
/// `#method in object` -> `_Class_brand.has(_checkInRHS(object))`
1833+
///
1834+
// `#[inline]` so that compiler sees that `expr` is an `Expression::PrivateFieldExpression`
1835+
#[inline]
1836+
pub(super) fn transform_private_in_expression(
1837+
&mut self,
1838+
expr: &mut Expression<'a>,
1839+
ctx: &mut TraverseCtx<'a>,
1840+
) {
1841+
let Expression::PrivateInExpression(private_in) = ctx.ast.move_expression(expr) else {
1842+
unreachable!();
1843+
};
1844+
1845+
*expr = self.transform_private_in_expression_impl(private_in, ctx);
1846+
}
1847+
1848+
fn transform_private_in_expression_impl(
1849+
&mut self,
1850+
private_field: ArenaBox<'a, PrivateInExpression<'a>>,
1851+
ctx: &mut TraverseCtx<'a>,
1852+
) -> Expression<'a> {
1853+
let PrivateInExpression { left, right, span, .. } = private_field.unbox();
1854+
1855+
let ResolvedPrivateProp { class_bindings, prop_binding, is_method, is_static, .. } =
1856+
self.classes_stack.find_private_prop(&left);
1857+
1858+
if is_static {
1859+
let class_binding = class_bindings.get_or_init_static_binding(ctx);
1860+
let class_ident = class_binding.create_read_expression(ctx);
1861+
let left = self.create_check_in_rhs(right, SPAN, ctx);
1862+
return ctx.ast.expression_binary(
1863+
span,
1864+
left,
1865+
BinaryOperator::StrictEquality,
1866+
class_ident,
1867+
);
1868+
}
1869+
1870+
let callee = if is_method {
1871+
class_bindings.brand().create_read_expression(ctx)
1872+
} else {
1873+
prop_binding.create_read_expression(ctx)
1874+
};
1875+
let callee = create_member_callee(callee, "has", ctx);
1876+
let argument = self.create_check_in_rhs(right, SPAN, ctx);
1877+
ctx.ast.expression_call(span, callee, NONE, ctx.ast.vec1(Argument::from(argument)), false)
1878+
}
1879+
18231880
/// Duplicate object to be used in get/set pair.
18241881
///
18251882
/// If `object` may have side effects, create a temp var `_object` and assign to it.
@@ -2151,4 +2208,19 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
21512208
let expressions = ctx.ast.vec_from_array([object, error]);
21522209
ctx.ast.expression_sequence(span, expressions)
21532210
}
2211+
2212+
/// _checkInRHS(object)
2213+
fn create_check_in_rhs(
2214+
&self,
2215+
object: Expression<'a>,
2216+
span: Span,
2217+
ctx: &mut TraverseCtx<'a>,
2218+
) -> Expression<'a> {
2219+
self.ctx.helper_call_expr(
2220+
Helper::CheckInRHS,
2221+
span,
2222+
ctx.ast.vec1(Argument::from(object)),
2223+
ctx,
2224+
)
2225+
}
21542226
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
var _Foo_brand = new WeakSet();
2+
class Foo {
3+
constructor() {
4+
babelHelpers.classPrivateMethodInitSpec(this, _Foo_brand);
5+
}
6+
test(other) {
7+
return _Foo_brand.has(babelHelpers.checkInRHS(other));
8+
}
9+
}
10+
function _get_foo() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class Foo {
2+
test(other) {
3+
return babelHelpers.checkInRHS(other) === Foo;
4+
}
5+
}
6+
function _get_foo() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
var _Foo_brand = new WeakSet();
2+
class Foo {
3+
constructor() {
4+
babelHelpers.classPrivateMethodInitSpec(this, _Foo_brand);
5+
}
6+
test(other) {
7+
return _Foo_brand.has(babelHelpers.checkInRHS(other));
8+
}
9+
}
10+
function _get_foo() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
var _F_brand = new WeakSet();
2+
var _x = new WeakMap();
3+
var _y = new WeakMap();
4+
class F {
5+
constructor() {
6+
babelHelpers.classPrivateMethodInitSpec(this, _F_brand);
7+
babelHelpers.classPrivateFieldInitSpec(this, _x, 0);
8+
babelHelpers.classPrivateFieldInitSpec(this, _y, (() => {
9+
throw "error";
10+
})());
11+
}
12+
m() {
13+
_F_brand.has(babelHelpers.checkInRHS(this));
14+
_x.has(babelHelpers.checkInRHS(this));
15+
_y.has(babelHelpers.checkInRHS(this));
16+
_F_brand.has(babelHelpers.checkInRHS(this));
17+
}
18+
}
19+
function _get_w() {}
20+
function _z() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class Foo {
2+
test(other) {
3+
return babelHelpers.checkInRHS(other) === Foo;
4+
}
5+
}
6+
function _get_foo() {}

tasks/transform_conformance/snapshots/babel.snap.md

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

3-
Passed: 661/1154
3+
Passed: 686/1154
44

55
# All Passed:
66
* babel-plugin-transform-logical-assignment-operators
@@ -811,7 +811,7 @@ x Output mismatch
811811
x Output mismatch
812812

813813

814-
# babel-plugin-transform-private-property-in-object (0/59)
814+
# babel-plugin-transform-private-property-in-object (25/59)
815815
* assumption-privateFieldsAsProperties/accessor/input.js
816816
x Output mismatch
817817

@@ -872,36 +872,6 @@ x Output mismatch
872872
* assumption-privateFieldsAsSymbols/static-method/input.js
873873
x Output mismatch
874874

875-
* private/accessor/input.js
876-
x Output mismatch
877-
878-
* private/field/input.js
879-
x Output mismatch
880-
881-
* private/method/input.js
882-
x Output mismatch
883-
884-
* private/native-classes/input.js
885-
x Output mismatch
886-
887-
* private/nested-class/input.js
888-
x Output mismatch
889-
890-
* private/nested-class-other-redeclared/input.js
891-
x Output mismatch
892-
893-
* private/nested-class-redeclared/input.js
894-
x Output mismatch
895-
896-
* private/static-accessor/input.js
897-
x Output mismatch
898-
899-
* private/static-field/input.js
900-
x Output mismatch
901-
902-
* private/static-method/input.js
903-
x Output mismatch
904-
905875
* private/static-shadow/input.js
906876
x Output mismatch
907877

@@ -914,9 +884,6 @@ x Output mismatch
914884
* private-loose/method/input.js
915885
x Output mismatch
916886

917-
* private-loose/native-classes/input.js
918-
x Output mismatch
919-
920887
* private-loose/nested-class/input.js
921888
x Output mismatch
922889

@@ -938,51 +905,9 @@ x Output mismatch
938905
* private-loose/static-shadow/input.js
939906
x Output mismatch
940907

941-
* to-native-fields/accessor/input.js
942-
x Output mismatch
943-
944908
* to-native-fields/class-expression-in-default-param/input.js
945909
x Output mismatch
946910

947-
* to-native-fields/class-expression-instance/input.js
948-
x Output mismatch
949-
950-
* to-native-fields/class-expression-static/input.js
951-
x Output mismatch
952-
953-
* to-native-fields/field/input.js
954-
x Output mismatch
955-
956-
* to-native-fields/half-constructed-instance/input.js
957-
x Output mismatch
958-
959-
* to-native-fields/half-constructed-static/input.js
960-
x Output mismatch
961-
962-
* to-native-fields/method/input.js
963-
x Output mismatch
964-
965-
* to-native-fields/multiple-checks/input.js
966-
x Output mismatch
967-
968-
* to-native-fields/nested-class/input.js
969-
x Output mismatch
970-
971-
* to-native-fields/nested-class-other-redeclared/input.js
972-
x Output mismatch
973-
974-
* to-native-fields/nested-class-redeclared/input.js
975-
x Output mismatch
976-
977-
* to-native-fields/static-accessor/input.js
978-
x Output mismatch
979-
980-
* to-native-fields/static-field/input.js
981-
x Output mismatch
982-
983-
* to-native-fields/static-method/input.js
984-
x Output mismatch
985-
986911
* to-native-fields/static-shadow/input.js
987912
x Output mismatch
988913

tasks/transform_conformance/snapshots/babel_exec.snap.md

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ commit: 54a8389f
22

33
node: v22.12.0
44

5-
Passed: 283 of 374 (75.67%)
5+
Passed: 291 of 374 (77.81%)
66

77
Failures:
88

@@ -78,9 +78,6 @@ AssertionError: expected '_Class' to be 'Foo' // Object.is equality
7878
AssertionError: expected '_Class' to be 'Foo' // Object.is equality
7979
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-static-infer-name-exec.test.js:9:19
8080

81-
./fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-loose-private-in-exec.test.js
82-
Private field '#bar' must be declared in an enclosing class
83-
8481
./fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-loose-private-methods-access-exec.test.js
8582
TypeError: attempted to use private field on non-instance
8683
at _classPrivateFieldBase (./node_modules/.pnpm/@babel+runtime@7.26.0/node_modules/@babel/runtime/helpers/classPrivateFieldLooseBase.js:2:44)
@@ -91,9 +88,6 @@ TypeError: attempted to use private field on non-instance
9188
AssertionError: expected [Function Base] to be undefined // Object.is equality
9289
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-new-target-exec.test.js:10:29
9390

94-
./fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-private-in-exec.test.js
95-
Private field '#bar' must be declared in an enclosing class
96-
9791
./fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js
9892
TypeError: Cannot read properties of undefined (reading 'x')
9993
at m (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js:10:16)
@@ -415,40 +409,32 @@ TypeError: "#privateStaticFieldValue" is write-only
415409
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-get-only-setter-exec.test.js:14:12
416410

417411
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsProperties-method-exec.test.js
418-
Private field '#foo' must be declared in an enclosing class
419-
420-
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsProperties-rhs-not-object-exec.test.js
421-
Private field '#p' must be declared in an enclosing class
422-
423-
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsSymbols-method-exec.test.js
424-
Private field '#foo' must be declared in an enclosing class
425-
426-
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsSymbols-rhs-not-object-exec.test.js
427-
Private field '#p' must be declared in an enclosing class
412+
ReferenceError: _Foo_brand is not defined
413+
at new Foo (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsProperties-method-exec.test.js:8:38)
414+
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsProperties-method-exec.test.js:19:13
428415

429416
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-loose-rhs-not-object-exec.test.js
430-
Private field '#p' must be declared in an enclosing class
417+
AssertionError: expected [Function] to throw error including 'right-hand side of \'in\' should be a…' but got '_Class_brand is not defined'
418+
at Proxy.<anonymous> (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:1438:21)
419+
at Proxy.<anonymous> (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:923:17)
420+
at Proxy.methodWrapper (./node_modules/.pnpm/chai@5.1.2/node_modules/chai/chai.js:1610:25)
421+
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-loose-rhs-not-object-exec.test.js:176:5
431422

432423
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-loose-static-shadow-exec.test.js
433-
Private field '#x' must be declared in an enclosing class
434-
435-
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-rhs-not-object-exec.test.js
436-
Private field '#p' must be declared in an enclosing class
424+
AssertionError: expected 2 to be 5 // Object.is equality
425+
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-loose-static-shadow-exec.test.js:18:25
437426

438427
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-static-shadow-exec.test.js
439-
Private field '#x' must be declared in an enclosing class
440-
441-
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-half-constructed-instance-exec.test.js
442-
Private field '#w' must be declared in an enclosing class
428+
AssertionError: expected 2 to be 5 // Object.is equality
429+
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-static-shadow-exec.test.js:18:25
443430

444431
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-half-constructed-static-exec.test.js
445-
Private field '#w' must be declared in an enclosing class
446-
447-
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-rhs-not-object-exec.test.js
448-
Private field '#p' must be declared in an enclosing class
432+
AssertionError: expected true to be false // Object.is equality
433+
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-half-constructed-static-exec.test.js:29:15
449434

450435
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-static-shadow-exec.test.js
451-
Private field '#x' must be declared in an enclosing class
436+
AssertionError: expected 2 to be 5 // Object.is equality
437+
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-static-shadow-exec.test.js:18:25
452438

453439
./fixtures/babel/babel-preset-env-test-fixtures-plugins-integration-issue-15170-exec.test.js
454440
AssertionError: expected [Function] to not throw an error but 'ReferenceError: x is not defined' was thrown

0 commit comments

Comments
 (0)