Skip to content

Commit

Permalink
refactor(transformer/class-properties): use duplicate_object in `tr…
Browse files Browse the repository at this point in the history
…ansform_expression_to_wrap_nullish_check` (#7664)

The `duplicate_object` is great, it has handled unbound identifiers and `this` expressions, so we can remove a lot of code. But it is still a little bit annoying because we need two `object` here,  although we can use `clone_in` it needs a special logic for `Expression::Identifier`.
  • Loading branch information
Dunqing committed Dec 5, 2024
1 parent f029090 commit 8883968
Showing 1 changed file with 14 additions and 39 deletions.
53 changes: 14 additions & 39 deletions crates/oxc_transformer/src/es2022/class_properties/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::mem;

use oxc_allocator::Box as ArenaBox;
use oxc_allocator::{Box as ArenaBox, CloneIn};
use oxc_ast::{ast::*, NONE};
use oxc_span::SPAN;
use oxc_syntax::{
Expand Down Expand Up @@ -1102,34 +1102,26 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
///
/// Returns:
/// * Bound Identifier: `A` -> `A === null || A === void 0`
/// * `this`: `this` -> `this === null || this === void 0`
/// * Unbound Identifier or anything else: `A.B` -> `(_A$B = A.B) === null || _A$B === void 0`
///
/// NOTE: This method will mutate the passed-in `object` to a temp variable identifier.
/// NOTE: This method will mutate the passed-in `object` to a second copy of
/// [`Self::duplicate_object`]'s return.
fn transform_expression_to_wrap_nullish_check(
&mut self,
object: &mut Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
// `A` -> `A === null || A === void 0`
if let Expression::Identifier(ident) = object {
if let Some(binding) = self.get_existing_binding_for_identifier(ident, ctx) {
let left1 = binding.create_read_expression(ctx);
let left2 = binding.create_read_expression(ctx);
return self.wrap_nullish_check(left1, left2, ctx);
}
}

// `A.B` -> `(_A$B = A.B) === null || _A$B === void 0`
// TODO: should add an API `generate_uid_in_current_hoist_scope_based_on_node` to instead this
let temp_var_binding = self.ctx.var_declarations.create_uid_var_based_on_node(object, ctx);

let object = mem::replace(object, temp_var_binding.create_read_expression(ctx));
let assignment = create_assignment(
&temp_var_binding,
Self::ensure_optional_expression_wrapped_by_chain_expression(object, ctx),
ctx,
);
let reference = temp_var_binding.create_read_expression(ctx);
let owned_object = ctx.ast.move_expression(object.get_inner_expression_mut());
let owned_object =
Self::ensure_optional_expression_wrapped_by_chain_expression(owned_object, ctx);
let (assignment, reference) = self.duplicate_object(owned_object, ctx);
// We cannot use `clone_in` to clone identifier reference because it will lose reference id
*object = if let Expression::Identifier(ident) = &reference {
MaybeBoundIdentifier::from_identifier_reference(ident, ctx).create_read_expression(ctx)
} else {
reference.clone_in(ctx.ast.allocator)
};
self.wrap_nullish_check(assignment, reference, ctx)
}

Expand Down Expand Up @@ -1159,23 +1151,6 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
}
}

/// Get an MaybeBoundIdentifier from an bound identifier reference.
///
/// If no temp variable required, returns `MaybeBoundIdentifier` for existing variable/global.
/// If temp variable is required, returns `None`.
fn get_existing_binding_for_identifier(
&self,
ident: &IdentifierReference<'a>,
ctx: &TraverseCtx<'a>,
) -> Option<MaybeBoundIdentifier<'a>> {
let binding = MaybeBoundIdentifier::from_identifier_reference(ident, ctx);
if self.ctx.assumptions.pure_getters || binding.to_bound_identifier().is_some() {
Some(binding)
} else {
None
}
}

/// Ensure that the expression is wrapped by a chain expression.
///
/// If the given expression contains optional expression, it will be wrapped by
Expand Down

0 comments on commit 8883968

Please sign in to comment.