Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions crates/oxc_transformer/src/es2022/class_properties/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,8 +959,13 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
if matches!(element, ChainElement::PrivateFieldExpression(_)) {
// 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 result =
self.transform_private_field_expression_of_chain_expression(&mut chain_expr, ctx);
let result = self
.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))
} else if let Some(result) = self.transform_chain_expression_element(element, ctx) {
let chain_expr = Self::convert_chain_expression_to_expression(expr, ctx);
Expand Down Expand Up @@ -1003,7 +1008,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
) -> Option<Expression<'a>> {
match expr {
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
.transform_member_expression_of_chain_expression(
Expand Down Expand Up @@ -1086,17 +1091,20 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
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(
&mut self,
expr: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
) -> Option<Expression<'a>> {
let Expression::PrivateFieldExpression(field_expr) = expr else { unreachable!() };

let is_optional = field_expr.optional;
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))
} else {
self.transform_first_optional_expression(object, ctx)
Expand All @@ -1110,13 +1118,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
self.transform_private_field_expression(expr, ctx);
}

left.unwrap_or_else(|| {
// `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)
})
result
}

fn transform_member_expression_of_chain_expression(
Expand Down Expand Up @@ -1145,6 +1147,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
let is_optional = call_expr.optional;

let callee = call_expr.callee.get_inner_expression_mut();
if matches!(callee, Expression::PrivateFieldExpression(_)) {
let result = self.transform_first_optional_expression(callee, ctx);
Expand All @@ -1157,9 +1160,8 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
return result;
}

let result = self.transform_chain_element_recursively(callee, ctx)?;
if !is_optional {
return Some(result);
return self.transform_chain_element_recursively(callee, ctx);
}

// `o?.Foo.#self.getSelf?.()?.self.#m();`
Expand All @@ -1168,6 +1170,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
// 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.
let result = self.transform_chain_element_recursively(callee, ctx)?;
let object = callee.to_member_expression_mut().object_mut();
let (assignment, context) = self.duplicate_object(ctx.ast.move_expression(object), ctx);
*object = assignment;
Expand Down
4 changes: 2 additions & 2 deletions tasks/transform_conformance/snapshots/oxc.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: 54a8389f

Passed: 106/120
Passed: 107/121

# All Passed:
* babel-plugin-transform-class-static-block
Expand All @@ -16,7 +16,7 @@ Passed: 106/120
* regexp


# babel-plugin-transform-class-properties (7/10)
# babel-plugin-transform-class-properties (8/11)
* private-loose-tagged-template/input.js
Scope children mismatch:
after transform: ScopeId(1): [ScopeId(2), ScopeId(3), ScopeId(4)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class A {
#a = {};
method() {
this.#a.get(message.id)?.(message);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading