Skip to content

Commit

Permalink
Feat: Computed keys
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Oct 31, 2024
1 parent 6fb6c39 commit 78b7bed
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 29 deletions.
1 change: 0 additions & 1 deletion crates/oxc_transformer/src/common/var_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ impl<'a> VarDeclarationsStore<'a> {

/// Add a `let` declaration to be inserted at top of current enclosing statement block,
/// given a `BoundIdentifier`.
#[expect(dead_code)]
pub fn insert_let(
&self,
binding: &BoundIdentifier<'a>,
Expand Down
69 changes: 55 additions & 14 deletions crates/oxc_transformer/src/es2022/class_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ impl<'a, 'ctx> Traverse<'a> for ClassProperties<'a, 'ctx> {
}
false
} else {
// TODO: If a method with computed key which is before last property with computed key,
// convert method key to temp var to preserve evaluations order for keys
true
}
});
Expand Down Expand Up @@ -195,37 +197,46 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
// Convert to assignment or `_defineProperty` call, depending on `loose` option
let init_expr = if self.set_public_class_fields {
// `this.foo = value`
let key = match &prop.key {
PropertyKey::StaticIdentifier(ident) => ident.as_ref().clone(),
_ => {
let this = ctx.ast.expression_this(SPAN);
let left = match &mut prop.key {
PropertyKey::StaticIdentifier(ident) => {
ctx.ast.member_expression_static(SPAN, this, ident.as_ref().clone(), false)
}
PropertyKey::PrivateIdentifier(_) => {
// TODO: Handle private properties
// TODO: Handle computed property key
ctx.ast.identifier_name(SPAN, Atom::from("oops"))
ctx.ast.member_expression_static(
SPAN,
this,
ctx.ast.identifier_name(SPAN, Atom::from("oops")),
false,
)
}
key @ match_expression!(PropertyKey) => {
let key = self.create_computed_key_temp_var(key.to_expression_mut(), ctx);
ctx.ast.member_expression_computed(SPAN, this, key, false)
}
};

// TODO: Should this have span of the original `PropertyDefinition`?
ctx.ast.expression_assignment(
SPAN,
AssignmentOperator::Assign,
AssignmentTarget::from(ctx.ast.member_expression_static(
SPAN,
ctx.ast.expression_this(SPAN),
key,
false,
)),
AssignmentTarget::from(left),
value,
)
} else {
// `_defineProperty(this, "foo", value)`
let key = match &prop.key {
let key = match &mut prop.key {
PropertyKey::StaticIdentifier(ident) => {
ctx.ast.expression_string_literal(ident.span, ident.name.clone())
}
_ => {
PropertyKey::PrivateIdentifier(_) => {
// TODO: Handle private properties
// TODO: Handle computed property key
ctx.ast.expression_string_literal(SPAN, Atom::from("oops"))
}
key @ match_expression!(PropertyKey) => {
self.create_computed_key_temp_var(key.to_expression_mut(), ctx)
}
};
let args = ctx.ast.vec_from_iter(
[ctx.ast.expression_this(SPAN), key, value].into_iter().map(Argument::from),
Expand All @@ -237,6 +248,36 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
instance_inits.push(init_expr);
}

/// Convert computed property/method key to a temp var.
///
/// Transformation is:
/// `class C { [x()] = 2; }` -> `let _x; _x = x(); class C { constructor() { this[_x] = 2; } }`
/// This function creates the `let _x;` and `_x = x();` statements and returns `_x`.
///
/// TODO: For class expressions, need to instead insert assignments before the expression.
/// `let C = class C { [x()] = 2; };`
/// -> `let _x; let C = (_x = x(), class C { constructor() { this[_x] = 2; } });`
fn create_computed_key_temp_var(
&mut self,
key: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let key = ctx.ast.move_expression(key);

// TODO: Bound vars and literals do not need temp vars
// e.g. `let x = 'x'; class C { [x] = 1; }` or `class C { ['x'] = 1; }`

let class_scope_id = ctx.current_scope_id();
let parent_scope_id = ctx.scopes().get_parent_id(class_scope_id).unwrap();
// TODO: `_x = ...` should be a separate statement/expression, not initialized in the `let` statement.
// TODO: Handle if is a class expression defined in a function's params.
let binding =
ctx.generate_uid_based_on_node(&key, parent_scope_id, SymbolFlags::BlockScopedVariable);
self.ctx.var_declarations.insert_let(&binding, Some(key), ctx);

binding.create_read_expression(ctx)
}

/// Convert static property to initialization expression.
#[expect(clippy::unused_self)]
fn convert_static_property(
Expand Down
19 changes: 5 additions & 14 deletions tasks/transform_conformance/snapshots/babel_exec.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: d20b314c

Passed: 46/225
Passed: 49/225

# All Passed:
* babel-plugin-transform-class-static-block
Expand All @@ -24,7 +24,7 @@ exec failed
exec failed


# babel-plugin-transform-class-properties (1/126)
# babel-plugin-transform-class-properties (4/126)
* assumption-noDocumentAll/optional-chain-before-member-call/exec.js
exec failed

Expand All @@ -37,9 +37,6 @@ exec failed
* assumption-setPublicClassFields/computed-initialization-order/exec.js
exec failed

* assumption-setPublicClassFields/instance-computed/exec.js
exec failed

* assumption-setPublicClassFields/static/exec.js
exec failed

Expand All @@ -58,9 +55,6 @@ exec failed
* assumption-setPublicClassFields/static-undefined/exec.js
exec failed

* class-name-tdz/general/exec.js
exec failed

* class-name-tdz/static-edgest-case/exec.js
exec failed

Expand Down Expand Up @@ -265,6 +259,9 @@ exec failed
* private-loose/nested-class-computed-redeclared/exec.js
exec failed

* private-loose/nested-class-extends-computed/exec.js
exec failed

* private-loose/nested-class-extends-computed-redeclared/exec.js
exec failed

Expand Down Expand Up @@ -370,9 +367,6 @@ exec failed
* public-loose/computed-initialization-order/exec.js
exec failed

* public-loose/instance-computed/exec.js
exec failed

* public-loose/static/exec.js
exec failed

Expand All @@ -391,9 +385,6 @@ exec failed
* public-loose/static-undefined/exec.js
exec failed

* regression/15098/exec.js
exec failed

* regression/7371/exec.js
exec failed

Expand Down

0 comments on commit 78b7bed

Please sign in to comment.