Skip to content

Commit 1617edd

Browse files
committed
refactor(transformer/class-properties): duplicate_object_twice method
1 parent 8883968 commit 1617edd

2 files changed

Lines changed: 78 additions & 20 deletions

File tree

crates/oxc_transformer/src/es2022/class_properties/private.rs

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,23 @@
33
44
use std::mem;
55

6-
use oxc_allocator::{Box as ArenaBox, CloneIn};
6+
use oxc_allocator::Box as ArenaBox;
77
use oxc_ast::{ast::*, NONE};
88
use oxc_span::SPAN;
99
use oxc_syntax::{
1010
reference::{ReferenceFlags, ReferenceId},
1111
symbol::SymbolId,
1212
};
1313
use oxc_traverse::{
14-
ast_operations::get_var_name_from_node, Ancestor, BoundIdentifier, MaybeBoundIdentifier,
15-
TraverseCtx,
14+
ast_operations::get_var_name_from_node, Ancestor, BoundIdentifier, TraverseCtx,
1615
};
1716

1817
use crate::common::helper_loader::Helper;
1918

2019
use super::{
2120
private_props::ResolvedPrivateProp,
2221
utils::{
23-
assert_expr_neither_parenthesis_nor_typescript_syntax, create_assignment,
22+
assert_expr_neither_parenthesis_nor_typescript_syntax, create_array, create_assignment,
2423
create_underscore_ident_name,
2524
},
2625
ClassProperties,
@@ -1106,7 +1105,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
11061105
/// * Unbound Identifier or anything else: `A.B` -> `(_A$B = A.B) === null || _A$B === void 0`
11071106
///
11081107
/// NOTE: This method will mutate the passed-in `object` to a second copy of
1109-
/// [`Self::duplicate_object`]'s return.
1108+
/// [`Self::duplicate_object_twice`]'s return.
11101109
fn transform_expression_to_wrap_nullish_check(
11111110
&mut self,
11121111
object: &mut Expression<'a>,
@@ -1115,14 +1114,9 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
11151114
let owned_object = ctx.ast.move_expression(object.get_inner_expression_mut());
11161115
let owned_object =
11171116
Self::ensure_optional_expression_wrapped_by_chain_expression(owned_object, ctx);
1118-
let (assignment, reference) = self.duplicate_object(owned_object, ctx);
1119-
// We cannot use `clone_in` to clone identifier reference because it will lose reference id
1120-
*object = if let Expression::Identifier(ident) = &reference {
1121-
MaybeBoundIdentifier::from_identifier_reference(ident, ctx).create_read_expression(ctx)
1122-
} else {
1123-
reference.clone_in(ctx.ast.allocator)
1124-
};
1125-
self.wrap_nullish_check(assignment, reference, ctx)
1117+
let (assignment, reference1, reference2) = self.duplicate_object_twice(owned_object, ctx);
1118+
*object = reference1;
1119+
self.wrap_nullish_check(assignment, reference2, ctx)
11261120
}
11271121

11281122
/// Converts chain expression to expression
@@ -1456,7 +1450,7 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
14561450
///
14571451
/// If `object` may have side effects, create a temp var `_object` and assign to it.
14581452
///
1459-
/// * `this` -> (`this`, `this`)
1453+
/// * `this` -> `this`, `this`
14601454
/// * Bound identifier `object` -> `object`, `object`
14611455
/// * Unbound identifier `object` -> `_object = object`, `_object`
14621456
/// * Anything else `foo()` -> `_foo = foo()`, `_foo`
@@ -1467,16 +1461,58 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
14671461
object: Expression<'a>,
14681462
ctx: &mut TraverseCtx<'a>,
14691463
) -> (Expression<'a>, Expression<'a>) {
1464+
let (object1, duplicates) = self.duplicate_object_multiple::<1>(object, ctx);
1465+
let [object2] = duplicates;
1466+
(object1, object2)
1467+
}
1468+
1469+
/// Duplicate object to be used in triple.
1470+
///
1471+
/// If `object` may have side effects, create a temp var `_object` and assign to it.
1472+
///
1473+
/// * `this` -> `this`, `this`, `this`
1474+
/// * Bound identifier `object` -> `object`, `object`, `object`
1475+
/// * Unbound identifier `object` -> `_object = object`, `_object`, `_object`
1476+
/// * Anything else `foo()` -> `_foo = foo()`, `_foo`, `_foo`
1477+
///
1478+
/// Returns 3 `Expression`s. The first must be inserted into output first.
1479+
fn duplicate_object_twice(
1480+
&mut self,
1481+
object: Expression<'a>,
1482+
ctx: &mut TraverseCtx<'a>,
1483+
) -> (Expression<'a>, Expression<'a>, Expression<'a>) {
1484+
let (object1, duplicates) = self.duplicate_object_multiple::<2>(object, ctx);
1485+
let [object2, object3] = duplicates;
1486+
(object1, object2, object3)
1487+
}
1488+
1489+
/// Duplicate object `N + 1` times.
1490+
///
1491+
/// If `object` may have side effects, create a temp var `_object` and assign to it.
1492+
///
1493+
/// * `this` -> `this`, [`this`; N]
1494+
/// * Bound identifier `object` -> `object`, [`object`; N]
1495+
/// * Unbound identifier `object` -> `_object = object`, [`_object`; N]
1496+
/// * Anything else `foo()` -> `_foo = foo()`, [`_foo`; N]
1497+
///
1498+
/// Returns `N + 1` `Expression`s. The first must be inserted into output first.
1499+
fn duplicate_object_multiple<const N: usize>(
1500+
&mut self,
1501+
object: Expression<'a>,
1502+
ctx: &mut TraverseCtx<'a>,
1503+
) -> (Expression<'a>, [Expression<'a>; N]) {
14701504
assert_expr_neither_parenthesis_nor_typescript_syntax(&object);
1505+
14711506
// TODO: Handle if in a function's params
14721507
let temp_var_binding = match &object {
14731508
Expression::Identifier(ident) => {
14741509
let reference = ctx.symbols_mut().get_reference_mut(ident.reference_id());
14751510
if let Some(symbol_id) = reference.symbol_id() {
14761511
// Reading bound identifier cannot have side effects, so no need for temp var
14771512
let binding = BoundIdentifier::new(ident.name.clone(), symbol_id);
1478-
let object1 = binding.create_spanned_read_expression(ident.span, ctx);
1479-
return (object1, object);
1513+
let duplicates =
1514+
create_array(|| binding.create_spanned_read_expression(ident.span, ctx));
1515+
return (object, duplicates);
14801516
}
14811517

14821518
// Previously `x += 1` (`x` read + write), but moving to `_x = x` (`x` read only)
@@ -1486,16 +1522,16 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
14861522
}
14871523
Expression::ThisExpression(this) => {
14881524
// Reading `this` cannot have side effects, so no need for temp var
1489-
let object1 = ctx.ast.expression_this(this.span);
1490-
return (object1, object);
1525+
let duplicates = create_array(|| ctx.ast.expression_this(this.span));
1526+
return (object, duplicates);
14911527
}
14921528
_ => self.ctx.var_declarations.create_uid_var_based_on_node(&object, ctx),
14931529
};
14941530

14951531
let object1 = create_assignment(&temp_var_binding, object, ctx);
1496-
let object2 = temp_var_binding.create_read_expression(ctx);
1532+
let references = create_array(|| temp_var_binding.create_read_expression(ctx));
14971533

1498-
(object1, object2)
1534+
(object1, references)
14991535
}
15001536

15011537
/// `_classPrivateFieldGet2(_prop, object)`

crates/oxc_transformer/src/es2022/class_properties/utils.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
//! ES2022: Class Properties
22
//! Utility functions.
33
4+
use std::{
5+
mem::{ManuallyDrop, MaybeUninit},
6+
ptr,
7+
};
8+
49
use oxc_ast::ast::*;
510
use oxc_span::SPAN;
611
use oxc_syntax::reference::ReferenceFlags;
@@ -61,3 +66,20 @@ pub(super) fn assert_expr_neither_parenthesis_nor_typescript_syntax(expr: &Expre
6166
"Should not be: {expr:?}",
6267
);
6368
}
69+
70+
/// Create array of length `N`, with each item initialized with provided function `init`.
71+
#[inline]
72+
pub(super) fn create_array<const N: usize, T, I: FnMut() -> T>(mut init: I) -> [T; N] {
73+
// https://github.com/rust-lang/rust/issues/62875#issuecomment-513834029
74+
// https://github.com/rust-lang/rust/issues/61956
75+
let mut array: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
76+
for elem in &mut array {
77+
elem.write(init());
78+
}
79+
// Wrapping in `ManuallyDrop` should not be necessary because `MaybeUninit` does not impl `Drop`,
80+
// but do it anyway just to make sure, as it's mentioned in issues above.
81+
let mut array = ManuallyDrop::new(array);
82+
// SAFETY: All elements of array are initialized.
83+
// `[MaybeUninit<T>; N]` and `[T; N]` have equivalent layout.
84+
unsafe { ptr::from_mut(&mut array).cast::<[T; N]>().read() }
85+
}

0 commit comments

Comments
 (0)