fix(transformer/class-properties): unwrap failed when private field expression doesn't contain optional expression in ChainExpression (#7798)

The root cause is due to transform wrongly a PrivateFieldExpression that doesn't contain any optional expression, so call `to_member_expression_mut` causes unwrap to fail.  I have fixed the incorrect transform and changed `to_member_expression_mut` to `as_member_expression_mut`.
This commit is contained in:
Dunqing 2024-12-11 11:32:21 +00:00
parent 6fa6785d0d
commit 2964a61546
4 changed files with 34 additions and 16 deletions

View file

@ -959,8 +959,13 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
if matches!(element, ChainElement::PrivateFieldExpression(_)) { if matches!(element, ChainElement::PrivateFieldExpression(_)) {
// The PrivateFieldExpression must be transformed, so we can convert it to a normal expression here. // The PrivateFieldExpression must be transformed, so we can convert it to a normal expression here.
let mut chain_expr = Self::convert_chain_expression_to_expression(expr, ctx); let mut chain_expr = Self::convert_chain_expression_to_expression(expr, ctx);
let result = let result = self
self.transform_private_field_expression_of_chain_expression(&mut chain_expr, ctx); .transform_private_field_expression_of_chain_expression(&mut chain_expr, ctx)
.unwrap_or_else(|| {
unreachable!(
"The ChainExpression must contains at least one optional expression, so it never be `None` here."
)
});
Some((result, chain_expr)) Some((result, chain_expr))
} else if let Some(result) = self.transform_chain_expression_element(element, ctx) { } else if let Some(result) = self.transform_chain_expression_element(element, ctx) {
let chain_expr = Self::convert_chain_expression_to_expression(expr, ctx); let chain_expr = Self::convert_chain_expression_to_expression(expr, ctx);
@ -1003,7 +1008,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
) -> Option<Expression<'a>> { ) -> Option<Expression<'a>> {
match expr { match expr {
Expression::PrivateFieldExpression(_) => { Expression::PrivateFieldExpression(_) => {
Some(self.transform_private_field_expression_of_chain_expression(expr, ctx)) self.transform_private_field_expression_of_chain_expression(expr, ctx)
} }
match_member_expression!(Expression) => self match_member_expression!(Expression) => self
.transform_member_expression_of_chain_expression( .transform_member_expression_of_chain_expression(
@ -1086,17 +1091,20 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
Some(result) Some(result)
} }
/// Transform private field expression of chain expression.
///
/// Returns `None` if the `expr` doesn't contain any optional expression.
fn transform_private_field_expression_of_chain_expression( fn transform_private_field_expression_of_chain_expression(
&mut self, &mut self,
expr: &mut Expression<'a>, expr: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> { ) -> Option<Expression<'a>> {
let Expression::PrivateFieldExpression(field_expr) = expr else { unreachable!() }; let Expression::PrivateFieldExpression(field_expr) = expr else { unreachable!() };
let is_optional = field_expr.optional; let is_optional = field_expr.optional;
let object = &mut field_expr.object; let object = &mut field_expr.object;
let left = if is_optional { let result = if is_optional {
Some(self.transform_expression_to_wrap_nullish_check(object, ctx)) Some(self.transform_expression_to_wrap_nullish_check(object, ctx))
} else { } else {
self.transform_first_optional_expression(object, ctx) self.transform_first_optional_expression(object, ctx)
@ -1110,13 +1118,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
self.transform_private_field_expression(expr, ctx); self.transform_private_field_expression(expr, ctx);
} }
left.unwrap_or_else(|| { result
// `o.Foo.#x?.self` -> `(_babelHelpers$assertC = expr === null || _babelHelpers$assertC === void 0 ?
// void 0 : _babelHelpers$assertC?.self;`
// `expr` is `o.Foo.#x` that has transformed to `babelHelpers.assertClassBrand(Foo, o.Foo, _x)._)` by
// `self.transform_private_field_expression`.
self.transform_expression_to_wrap_nullish_check(expr, ctx)
})
} }
fn transform_member_expression_of_chain_expression( fn transform_member_expression_of_chain_expression(
@ -1145,6 +1147,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
ctx: &mut TraverseCtx<'a>, ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> { ) -> Option<Expression<'a>> {
let is_optional = call_expr.optional; let is_optional = call_expr.optional;
let callee = call_expr.callee.get_inner_expression_mut(); let callee = call_expr.callee.get_inner_expression_mut();
if matches!(callee, Expression::PrivateFieldExpression(_)) { if matches!(callee, Expression::PrivateFieldExpression(_)) {
let result = self.transform_first_optional_expression(callee, ctx); let result = self.transform_first_optional_expression(callee, ctx);
@ -1157,9 +1160,8 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
return result; return result;
} }
let result = self.transform_chain_element_recursively(callee, ctx)?;
if !is_optional { if !is_optional {
return Some(result); return self.transform_chain_element_recursively(callee, ctx);
} }
// `o?.Foo.#self.getSelf?.()?.self.#m();` // `o?.Foo.#self.getSelf?.()?.self.#m();`
@ -1168,6 +1170,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
// then use it as a first argument of `getSelf` call. // then use it as a first argument of `getSelf` call.
// //
// TODO(improve-on-babel): Consider remove this logic, because it seems no runtime behavior change. // TODO(improve-on-babel): Consider remove this logic, because it seems no runtime behavior change.
let result = self.transform_chain_element_recursively(callee, ctx)?;
let object = callee.to_member_expression_mut().object_mut(); let object = callee.to_member_expression_mut().object_mut();
let (assignment, context) = self.duplicate_object(ctx.ast.move_expression(object), ctx); let (assignment, context) = self.duplicate_object(ctx.ast.move_expression(object), ctx);
*object = assignment; *object = assignment;

View file

@ -1,6 +1,6 @@
commit: 54a8389f commit: 54a8389f
Passed: 106/120 Passed: 107/121
# All Passed: # All Passed:
* babel-plugin-transform-class-static-block * babel-plugin-transform-class-static-block
@ -16,7 +16,7 @@ Passed: 106/120
* regexp * regexp
# babel-plugin-transform-class-properties (7/10) # babel-plugin-transform-class-properties (8/11)
* private-loose-tagged-template/input.js * private-loose-tagged-template/input.js
Scope children mismatch: Scope children mismatch:
after transform: ScopeId(1): [ScopeId(2), ScopeId(3), ScopeId(4)] after transform: ScopeId(1): [ScopeId(2), ScopeId(3), ScopeId(4)]

View file

@ -0,0 +1,6 @@
class A {
#a = {};
method() {
this.#a.get(message.id)?.(message);
}
}

View file

@ -0,0 +1,9 @@
var _a = /*#__PURE__*/ new WeakMap();
class A {
constructor() {
babelHelpers.classPrivateFieldInitSpec(this, _a, {});
}
method() {
babelHelpers.classPrivateFieldGet2(_a, this).get(message.id)?.(message);
}
}