mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(transformer/class-properties): transform private in expression (#8202)
This PR support transforms `#prop in object` in class-properties to cover https://www.npmjs.com/package/@babel/plugin-transform-private-property-in-object plugin does
This commit is contained in:
parent
80c16526fb
commit
0592a8b43f
10 changed files with 150 additions and 109 deletions
|
|
@ -163,6 +163,7 @@ pub enum Helper {
|
|||
SuperPropSet,
|
||||
ReadOnlyError,
|
||||
WriteOnlyError,
|
||||
CheckInRHS,
|
||||
}
|
||||
|
||||
impl Helper {
|
||||
|
|
@ -191,6 +192,7 @@ impl Helper {
|
|||
Self::SuperPropSet => "superPropSet",
|
||||
Self::ReadOnlyError => "readOnlyError",
|
||||
Self::WriteOnlyError => "writeOnlyError",
|
||||
Self::CheckInRHS => "checkInRHS",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -368,6 +368,10 @@ impl<'a, 'ctx> Traverse<'a> for ClassProperties<'a, 'ctx> {
|
|||
Expression::TaggedTemplateExpression(_) => {
|
||||
self.transform_tagged_template_expression(expr, ctx);
|
||||
}
|
||||
// "#prop in object"
|
||||
Expression::PrivateInExpression(_) => {
|
||||
self.transform_private_in_expression(expr, ctx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use std::mem;
|
||||
|
||||
use oxc_allocator::String as ArenaString;
|
||||
use oxc_allocator::{Box as ArenaBox, String as ArenaString};
|
||||
use oxc_ast::{ast::*, NONE};
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::{reference::ReferenceId, symbol::SymbolId};
|
||||
|
|
@ -1820,6 +1820,63 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
|
|||
*target = AssignmentTarget::from(replacement.into_member_expression());
|
||||
}
|
||||
|
||||
/// Transform private field in expression.
|
||||
///
|
||||
/// * Static
|
||||
/// `#prop in object` -> `_checkInRHS(object) === Class`
|
||||
///
|
||||
/// * Instance prop
|
||||
/// `#prop in object` -> `_prop.has(_checkInRHS(object))`
|
||||
///
|
||||
/// * Instance method
|
||||
/// `#method in object` -> `_Class_brand.has(_checkInRHS(object))`
|
||||
///
|
||||
// `#[inline]` so that compiler sees that `expr` is an `Expression::PrivateFieldExpression`
|
||||
#[inline]
|
||||
pub(super) fn transform_private_in_expression(
|
||||
&mut self,
|
||||
expr: &mut Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
let Expression::PrivateInExpression(private_in) = ctx.ast.move_expression(expr) else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
*expr = self.transform_private_in_expression_impl(private_in, ctx);
|
||||
}
|
||||
|
||||
fn transform_private_in_expression_impl(
|
||||
&mut self,
|
||||
private_field: ArenaBox<'a, PrivateInExpression<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let PrivateInExpression { left, right, span, .. } = private_field.unbox();
|
||||
|
||||
let ResolvedPrivateProp { class_bindings, prop_binding, is_method, is_static, .. } =
|
||||
self.classes_stack.find_private_prop(&left);
|
||||
|
||||
if is_static {
|
||||
let class_binding = class_bindings.get_or_init_static_binding(ctx);
|
||||
let class_ident = class_binding.create_read_expression(ctx);
|
||||
let left = self.create_check_in_rhs(right, SPAN, ctx);
|
||||
return ctx.ast.expression_binary(
|
||||
span,
|
||||
left,
|
||||
BinaryOperator::StrictEquality,
|
||||
class_ident,
|
||||
);
|
||||
}
|
||||
|
||||
let callee = if is_method {
|
||||
class_bindings.brand().create_read_expression(ctx)
|
||||
} else {
|
||||
prop_binding.create_read_expression(ctx)
|
||||
};
|
||||
let callee = create_member_callee(callee, "has", ctx);
|
||||
let argument = self.create_check_in_rhs(right, SPAN, ctx);
|
||||
ctx.ast.expression_call(span, callee, NONE, ctx.ast.vec1(Argument::from(argument)), false)
|
||||
}
|
||||
|
||||
/// Duplicate object to be used in get/set pair.
|
||||
///
|
||||
/// 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> {
|
|||
let expressions = ctx.ast.vec_from_array([object, error]);
|
||||
ctx.ast.expression_sequence(span, expressions)
|
||||
}
|
||||
|
||||
/// _checkInRHS(object)
|
||||
fn create_check_in_rhs(
|
||||
&self,
|
||||
object: Expression<'a>,
|
||||
span: Span,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
self.ctx.helper_call_expr(
|
||||
Helper::CheckInRHS,
|
||||
span,
|
||||
ctx.ast.vec1(Argument::from(object)),
|
||||
ctx,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
var _Foo_brand = new WeakSet();
|
||||
class Foo {
|
||||
constructor() {
|
||||
babelHelpers.classPrivateMethodInitSpec(this, _Foo_brand);
|
||||
}
|
||||
test(other) {
|
||||
return _Foo_brand.has(babelHelpers.checkInRHS(other));
|
||||
}
|
||||
}
|
||||
function _get_foo() {}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class Foo {
|
||||
test(other) {
|
||||
return babelHelpers.checkInRHS(other) === Foo;
|
||||
}
|
||||
}
|
||||
function _get_foo() {}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
var _Foo_brand = new WeakSet();
|
||||
class Foo {
|
||||
constructor() {
|
||||
babelHelpers.classPrivateMethodInitSpec(this, _Foo_brand);
|
||||
}
|
||||
test(other) {
|
||||
return _Foo_brand.has(babelHelpers.checkInRHS(other));
|
||||
}
|
||||
}
|
||||
function _get_foo() {}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
var _F_brand = new WeakSet();
|
||||
var _x = new WeakMap();
|
||||
var _y = new WeakMap();
|
||||
class F {
|
||||
constructor() {
|
||||
babelHelpers.classPrivateMethodInitSpec(this, _F_brand);
|
||||
babelHelpers.classPrivateFieldInitSpec(this, _x, 0);
|
||||
babelHelpers.classPrivateFieldInitSpec(this, _y, (() => {
|
||||
throw "error";
|
||||
})());
|
||||
}
|
||||
m() {
|
||||
_F_brand.has(babelHelpers.checkInRHS(this));
|
||||
_x.has(babelHelpers.checkInRHS(this));
|
||||
_y.has(babelHelpers.checkInRHS(this));
|
||||
_F_brand.has(babelHelpers.checkInRHS(this));
|
||||
}
|
||||
}
|
||||
function _get_w() {}
|
||||
function _z() {}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class Foo {
|
||||
test(other) {
|
||||
return babelHelpers.checkInRHS(other) === Foo;
|
||||
}
|
||||
}
|
||||
function _get_foo() {}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
commit: 54a8389f
|
||||
|
||||
Passed: 661/1154
|
||||
Passed: 686/1154
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-logical-assignment-operators
|
||||
|
|
@ -811,7 +811,7 @@ x Output mismatch
|
|||
x Output mismatch
|
||||
|
||||
|
||||
# babel-plugin-transform-private-property-in-object (0/59)
|
||||
# babel-plugin-transform-private-property-in-object (25/59)
|
||||
* assumption-privateFieldsAsProperties/accessor/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
|
@ -872,36 +872,6 @@ x Output mismatch
|
|||
* assumption-privateFieldsAsSymbols/static-method/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/accessor/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/field/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/method/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/native-classes/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/nested-class/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/nested-class-other-redeclared/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/nested-class-redeclared/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/static-accessor/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/static-field/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/static-method/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private/static-shadow/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
|
@ -914,9 +884,6 @@ x Output mismatch
|
|||
* private-loose/method/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private-loose/native-classes/input.js
|
||||
x Output mismatch
|
||||
|
||||
* private-loose/nested-class/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
|
@ -938,51 +905,9 @@ x Output mismatch
|
|||
* private-loose/static-shadow/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/accessor/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/class-expression-in-default-param/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/class-expression-instance/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/class-expression-static/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/field/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/half-constructed-instance/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/half-constructed-static/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/method/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/multiple-checks/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/nested-class/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/nested-class-other-redeclared/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/nested-class-redeclared/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/static-accessor/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/static-field/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/static-method/input.js
|
||||
x Output mismatch
|
||||
|
||||
* to-native-fields/static-shadow/input.js
|
||||
x Output mismatch
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ commit: 54a8389f
|
|||
|
||||
node: v22.12.0
|
||||
|
||||
Passed: 283 of 374 (75.67%)
|
||||
Passed: 291 of 374 (77.81%)
|
||||
|
||||
Failures:
|
||||
|
||||
|
|
@ -78,9 +78,6 @@ AssertionError: expected '_Class' to be 'Foo' // Object.is equality
|
|||
AssertionError: expected '_Class' to be 'Foo' // Object.is equality
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-static-infer-name-exec.test.js:9:19
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-loose-private-in-exec.test.js
|
||||
Private field '#bar' must be declared in an enclosing class
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-loose-private-methods-access-exec.test.js
|
||||
TypeError: attempted to use private field on non-instance
|
||||
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
|
|||
AssertionError: expected [Function Base] to be undefined // Object.is equality
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-new-target-exec.test.js:10:29
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-private-in-exec.test.js
|
||||
Private field '#bar' must be declared in an enclosing class
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-optional-chaining-test-fixtures-assumption-noDocumentAll-parenthesized-expression-member-call-exec.test.js
|
||||
TypeError: Cannot read properties of undefined (reading 'x')
|
||||
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
|
|||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-static-accessors-privateFieldsAsSymbols-get-only-setter-exec.test.js:14:12
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsProperties-method-exec.test.js
|
||||
Private field '#foo' must be declared in an enclosing class
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsProperties-rhs-not-object-exec.test.js
|
||||
Private field '#p' must be declared in an enclosing class
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsSymbols-method-exec.test.js
|
||||
Private field '#foo' must be declared in an enclosing class
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsSymbols-rhs-not-object-exec.test.js
|
||||
Private field '#p' must be declared in an enclosing class
|
||||
ReferenceError: _Foo_brand is not defined
|
||||
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)
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-assumption-privateFieldsAsProperties-method-exec.test.js:19:13
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-loose-rhs-not-object-exec.test.js
|
||||
Private field '#p' must be declared in an enclosing class
|
||||
AssertionError: expected [Function] to throw error including 'right-hand side of \'in\' should be a…' but got '_Class_brand is not defined'
|
||||
at Proxy.<anonymous> (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:1438:21)
|
||||
at Proxy.<anonymous> (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:923:17)
|
||||
at Proxy.methodWrapper (./node_modules/.pnpm/chai@5.1.2/node_modules/chai/chai.js:1610:25)
|
||||
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
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-loose-static-shadow-exec.test.js
|
||||
Private field '#x' must be declared in an enclosing class
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-rhs-not-object-exec.test.js
|
||||
Private field '#p' must be declared in an enclosing class
|
||||
AssertionError: expected 2 to be 5 // Object.is equality
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-loose-static-shadow-exec.test.js:18:25
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-static-shadow-exec.test.js
|
||||
Private field '#x' must be declared in an enclosing class
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-half-constructed-instance-exec.test.js
|
||||
Private field '#w' must be declared in an enclosing class
|
||||
AssertionError: expected 2 to be 5 // Object.is equality
|
||||
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-private-static-shadow-exec.test.js:18:25
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-half-constructed-static-exec.test.js
|
||||
Private field '#w' must be declared in an enclosing class
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-rhs-not-object-exec.test.js
|
||||
Private field '#p' must be declared in an enclosing class
|
||||
AssertionError: expected true to be false // Object.is equality
|
||||
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
|
||||
|
||||
./fixtures/babel/babel-plugin-transform-private-property-in-object-test-fixtures-to-native-fields-static-shadow-exec.test.js
|
||||
Private field '#x' must be declared in an enclosing class
|
||||
AssertionError: expected 2 to be 5 // Object.is equality
|
||||
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
|
||||
|
||||
./fixtures/babel/babel-preset-env-test-fixtures-plugins-integration-issue-15170-exec.test.js
|
||||
AssertionError: expected [Function] to not throw an error but 'ReferenceError: x is not defined' was thrown
|
||||
|
|
|
|||
Loading…
Reference in a new issue