feat(transformer/class-properties): transform callee which invokes private method (#8100)

transform call expression's callee if it invokes a private method
This commit is contained in:
Dunqing 2024-12-31 12:30:52 +00:00
parent 13349ef1b8
commit f14567a658
4 changed files with 78 additions and 77 deletions

View file

@ -447,15 +447,16 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
} else {
// TODO: Only call `insert_many_before` if some private *instance* props
let mut weakmap_symbol_id = None;
let has_method = false;
let mut has_method = false;
self.ctx.statement_injector.insert_many_before(
&stmt_address,
private_props.values().filter_map(|prop| {
if prop.is_static || has_method || prop.is_accessor {
if prop.is_static || (prop.is_method && has_method) || prop.is_accessor {
return None;
}
if prop.is_method {
// `var _C_brand = new WeakSet();`
has_method = true;
let binding = class_details.bindings.brand();
let value = create_new_weakset(ctx);
Some(create_variable_declaration(binding, value, ctx))
@ -587,11 +588,21 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
}));
} else {
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 && has_method) || prop.is_accessor {
return None;
}
if prop.is_method {
has_method = true;
// `_C_brand = new WeakSet()`
let binding = class_details.bindings.brand();
self.ctx.var_declarations.insert_var(binding, ctx);
let value = create_new_weakset(ctx);
return Some(create_assignment(binding, value, ctx));
}
// Insert `var _prop;` declaration
self.ctx.var_declarations.insert_var(&prop.binding, ctx);

View file

@ -206,9 +206,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
return;
}
let Some((callee, object)) = self.transform_private_field_callee(field_expr, ctx) else {
return;
};
let (callee, object) = self.transform_private_field_callee(field_expr, ctx);
Self::substitute_callee_and_insert_context(call_expr, callee, object, ctx);
}
@ -260,7 +258,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
&mut self,
field_expr: &mut PrivateFieldExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<(Expression<'a>, Expression<'a>)> {
) -> (Expression<'a>, Expression<'a>) {
let ResolvedPrivateProp {
prop_binding,
class_bindings,
@ -270,12 +268,15 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
is_declaration,
} = self.classes_stack.find_private_prop(&field_expr.field);
if is_method || is_accessor {
return None;
};
let prop_ident = prop_binding.create_read_expression(ctx);
if is_method || is_accessor {
return (
self.create_assert_class_brand_for_private_method(prop_ident, ctx),
ctx.ast.expression_this(SPAN),
);
};
// `(object.#method)()`
// ^^^^^^^^^^^^^^^^ is a parenthesized expression
let object = ctx.ast.move_expression(field_expr.object.get_inner_expression_mut());
@ -330,7 +331,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
(get_call, object2)
};
Some(replacement)
replacement
}
/// Transform assignment to private field.
@ -1152,7 +1153,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
if matches!(ctx.ancestor(1), Ancestor::CallExpressionCallee(_)) {
// `(Foo?.#m)();` -> `(Foo === null || Foo === void 0 ? void 0 : _m._.bind(Foo))();`
// ^^^^^^^^^^^^ is a call expression, we need to bind the proper context
*expr = self.transform_bindable_private_field(field_expr, ctx).unwrap();
*expr = self.transform_bindable_private_field(field_expr, ctx);
} else {
self.transform_private_field_expression(expr, ctx);
}
@ -1573,7 +1574,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
);
Some(Expression::from(replacement))
} else {
self.transform_bindable_private_field(field_expr, ctx)
Some(self.transform_bindable_private_field(field_expr, ctx))
}
}
@ -1581,8 +1582,8 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
&mut self,
field_expr: &mut PrivateFieldExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
let (callee, context) = self.transform_private_field_callee(field_expr, ctx)?;
) -> Expression<'a> {
let (callee, context) = self.transform_private_field_callee(field_expr, ctx);
// Return `<callee>.bind(object)`, to be substituted as tag of tagged template expression
let callee = Expression::from(ctx.ast.member_expression_static(
@ -1592,7 +1593,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
false,
));
let arguments = ctx.ast.vec1(Argument::from(context));
Some(ctx.ast.expression_call(field_expr.span, callee, NONE, arguments, false))
ctx.ast.expression_call(field_expr.span, callee, NONE, arguments, false)
}
/// Transform private field in assignment pattern.
@ -1785,6 +1786,18 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
)
}
/// `_assertClassBrand(_Class_brand, object, _prop)`
#[inline]
fn create_assert_class_brand_for_private_method(
&self,
value_or_prop_ident: Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let class_ident = self.current_class().bindings.brand().create_read_expression(ctx);
let object = ctx.ast.expression_this(SPAN);
self.create_assert_class_brand(class_ident, object, value_or_prop_ident, ctx)
}
/// `_assertClassBrand(Class, object, _prop)._`
fn create_assert_class_brand_underscore(
&self,

View file

@ -1,6 +1,6 @@
commit: 54a8389f
Passed: 623/1095
Passed: 628/1095
# All Passed:
* babel-plugin-transform-logical-assignment-operators
@ -462,7 +462,7 @@ x Output mismatch
x Output mismatch
# babel-plugin-transform-private-methods (8/148)
# babel-plugin-transform-private-methods (13/148)
* accessors/arguments/input.js
x Output mismatch
@ -565,15 +565,6 @@ x Output mismatch
* misc/multiple/input.js
x Output mismatch
* private-method/assignment/input.js
x Output mismatch
* private-method/async/input.js
x Output mismatch
* private-method/before-fields/input.js
x Output mismatch
* private-method/class-binding/input.js
x Output mismatch
@ -589,9 +580,6 @@ x Output mismatch
* private-method/exfiltrated/input.js
x Output mismatch
* private-method/generator/input.js
x Output mismatch
* private-method/read-only/input.js
x Output mismatch
@ -601,9 +589,6 @@ x Output mismatch
* private-method/super/input.js
x Output mismatch
* private-method/tagged-template/input.js
x Output mismatch
* private-method-loose/assignment/input.js
x Output mismatch

View file

@ -2,7 +2,7 @@ commit: 54a8389f
node: v22.12.0
Passed: 215 of 362 (59.39%)
Passed: 225 of 362 (62.15%)
Failures:
@ -62,9 +62,6 @@ TypeError: e.has is not a function
at Function.method (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js:12:11)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-static-shadow-exec.test.js:16:14
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-private-tagged-template-exec.test.js
Private field '#tag' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-class-properties-test-fixtures-public-computed-toPrimitive-exec.test.js
AssertionError: expected [Function] to throw error including '@@toPrimitive must return a primitive…' but got 'Cannot convert object to primitive va…'
at Proxy.<anonymous> (./node_modules/.pnpm/@vitest+expect@2.1.2/node_modules/@vitest/expect/dist/index.js:1438:21)
@ -98,7 +95,9 @@ AssertionError: expected [Function Base] to be undefined // Object.is equality
Private field '#bar' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-private-methods-access-exec.test.js
Private field '#foo' must be declared in an enclosing class
ReferenceError: _Foo_brand is not defined
at getFoo (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-private-methods-access-exec.test.js:17:35)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-class-static-block-test-fixtures-integration-private-methods-access-exec.test.js:18:9
./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')
@ -196,23 +195,15 @@ Private field '#privateFieldValue' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-set-only-getter-exec.test.js
Private field '#privateFieldValue' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-tagged-template-exec.test.js
Private field '#tag' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-updates-bigint-exec.test.js
Private field '#privateFieldValue' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-accessors-updates-exec.test.js
Private field '#privateFieldValue' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-assignment-exec.test.js
Private field '#privateMethod' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-before-fields-exec.test.js
Private field '#method' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-class-binding-exec.test.js
Private field '#getA' must be declared in an enclosing class
AssertionError: expected null to be [Function A] // Object.is equality
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-class-binding-exec.test.js:20:28
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-context-exec.test.js
Private field '#getStatus' must be declared in an enclosing class
@ -220,9 +211,6 @@ Private field '#getStatus' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-exfiltrated-exec.test.js
Private field '#privateMethod' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-generator-exec.test.js
Private field '#foo' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-loose-assignment-exec.test.js
Private field '#privateMethod' must be declared in an enclosing class
@ -268,14 +256,9 @@ Private field '#foo' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsProperties-super-exec.test.js
Invalid access to super
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-assignment-exec.test.js
Private field '#privateMethod' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-before-fields-exec.test.js
Private field '#method' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-class-binding-exec.test.js
Private field '#getA' must be declared in an enclosing class
AssertionError: expected null to be [Function A] // Object.is equality
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-class-binding-exec.test.js:20:28
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-context-exec.test.js
Private field '#getStatus' must be declared in an enclosing class
@ -283,9 +266,6 @@ Private field '#getStatus' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-exfiltrated-exec.test.js
Private field '#privateMethod' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-generator-exec.test.js
Private field '#foo' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-privateFieldsAsSymbols-super-exec.test.js
Invalid access to super
@ -295,26 +275,26 @@ Private field '#method' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-reassignment-exec.test.js
Private field '#privateFieldValue' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-scopable-exec.test.js
Private field '#privateMethodA' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-super-exec.test.js
Invalid access to super
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-method-tagged-template-exec.test.js
Private field '#tag' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-basic-exec.test.js
Private field '#privateStaticMethod' must be declared in an enclosing class
ReferenceError: _Cl_brand is not defined
at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-basic-exec.test.js:21:38)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-basic-exec.test.js:28:9
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-class-check-exec.test.js
Private field '#privateStaticMethod' must be declared in an enclosing class
ReferenceError: _Cl_brand is not defined
at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-class-check-exec.test.js:8:38)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-class-check-exec.test.js:17:13
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-exfiltrated-exec.test.js
Private field '#privateStaticMethod' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-generator-exec.test.js
Private field '#foo' must be declared in an enclosing class
ReferenceError: _Cl_brand is not defined
at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-generator-exec.test.js:8:38)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-generator-exec.test.js:18:14
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-loose-basic-exec.test.js
Private field '#privateStaticMethod' must be declared in an enclosing class
@ -365,22 +345,30 @@ Invalid access to super
Invalid access to super
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-basic-exec.test.js
Private field '#privateStaticMethod' must be declared in an enclosing class
ReferenceError: _Cl_brand is not defined
at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-basic-exec.test.js:21:38)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-basic-exec.test.js:28:9
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-class-check-exec.test.js
Private field '#privateStaticMethod' must be declared in an enclosing class
ReferenceError: _Cl_brand is not defined
at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-class-check-exec.test.js:8:38)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-class-check-exec.test.js:17:13
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-exfiltrated-exec.test.js
Private field '#privateStaticMethod' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-generator-exec.test.js
Private field '#foo' must be declared in an enclosing class
ReferenceError: _Cl_brand is not defined
at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-generator-exec.test.js:8:38)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-generator-exec.test.js:18:14
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-reassignment-exec.test.js
Private field '#privateStaticMethod' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-scopable-exec.test.js
Private field '#privateMethodA' must be declared in an enclosing class
ReferenceError: _Cl_brand is not defined
at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-scopable-exec.test.js:8:38)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-scopable-exec.test.js:22:9
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-privateFieldsAsSymbols-super-exec.test.js
Invalid access to super
@ -395,13 +383,17 @@ Private field '#method' must be declared in an enclosing class
Private field '#privateStaticMethod' must be declared in an enclosing class
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-scopable-exec.test.js
Private field '#privateMethodA' must be declared in an enclosing class
ReferenceError: _Cl_brand is not defined
at new Cl (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-scopable-exec.test.js:8:38)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-scopable-exec.test.js:22:9
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-super-exec.test.js
Invalid access to super
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-tagged-template-exec.test.js
Private field '#tag' must be declared in an enclosing class
ReferenceError: _Foo_brand is not defined
at Function.getReceiver (./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-tagged-template-exec.test.js:11:29)
at ./tasks/transform_conformance/fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-tagged-template-exec.test.js:17:13
./fixtures/babel/babel-plugin-transform-private-methods-test-fixtures-private-static-method-this-exec.test.js
Invalid access to super