Skip to content

Commit c83dad6

Browse files
committed
refactor(transformer/class-properties): streamline handling scope of instance property initializer (#10492)
Move the `instance_inits_scope_id` and `instance_inits_constructor_scope_id` fields from the `ClassProperties` struct to local variables. Using struct-level fields as temporary variables is unnecessary within three function depths, and you also need to consider when to reset the states, otherwise, you may cause a bug due to using previous temporary information. Moreover, I think it also improves a bit readability. NOTE: This is not only a refactor, but also along with #10495 and #10493 to prepare #10491.
1 parent 227febf commit c83dad6

File tree

5 files changed

+63
-33
lines changed

5 files changed

+63
-33
lines changed

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

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -239,23 +239,33 @@ impl<'a> ClassProperties<'a, '_> {
239239
return;
240240
}
241241

242+
// Scope that instance property initializers will be inserted into.
243+
// This is usually class constructor, but can also be a `_super` function which is created.
244+
let instance_inits_scope_id;
245+
// Scope of class constructor, if instance property initializers will be inserted into constructor.
246+
// Used for checking for variable name clashes.
247+
// e.g. `class C { prop = x(); constructor(x) {} }`
248+
// - `x` in constructor needs to be renamed when `x()` is moved into constructor body.
249+
// `None` if class has no existing constructor, as then there can't be any clashes.
250+
let mut instance_inits_constructor_scope_id = None;
251+
242252
// Determine where to insert instance property initializers in constructor
243253
let instance_inits_insert_location = if let Some(constructor) = constructor {
244254
// Existing constructor
245255
let constructor = constructor.value.as_mut();
246256
if has_super_class {
247257
let (insert_scopes, insert_location) =
248258
Self::replace_super_in_constructor(constructor, ctx);
249-
self.instance_inits_scope_id = insert_scopes.insert_in_scope_id;
250-
self.instance_inits_constructor_scope_id = insert_scopes.constructor_scope_id;
259+
instance_inits_scope_id = insert_scopes.insert_in_scope_id;
260+
instance_inits_constructor_scope_id = insert_scopes.constructor_scope_id;
251261
insert_location
252262
} else {
253263
let constructor_scope_id = constructor.scope_id();
254-
self.instance_inits_scope_id = constructor_scope_id;
264+
instance_inits_scope_id = constructor_scope_id;
255265
// Only record `constructor_scope_id` if constructor's scope has some bindings.
256266
// If it doesn't, no need to check for shadowed symbols in instance prop initializers,
257267
// because no bindings to clash with.
258-
self.instance_inits_constructor_scope_id =
268+
instance_inits_constructor_scope_id =
259269
if ctx.scoping().get_bindings(constructor_scope_id).is_empty() {
260270
None
261271
} else {
@@ -270,8 +280,7 @@ impl<'a> ClassProperties<'a, '_> {
270280
NodeId::DUMMY,
271281
ScopeFlags::Function | ScopeFlags::Constructor | ScopeFlags::StrictMode,
272282
);
273-
self.instance_inits_scope_id = constructor_scope_id;
274-
self.instance_inits_constructor_scope_id = None;
283+
instance_inits_scope_id = constructor_scope_id;
275284
InstanceInitsInsertLocation::NewConstructor
276285
};
277286

@@ -300,7 +309,13 @@ impl<'a> ClassProperties<'a, '_> {
300309
match element {
301310
ClassElement::PropertyDefinition(prop) => {
302311
if !prop.r#static {
303-
self.convert_instance_property(prop, &mut instance_inits, ctx);
312+
self.convert_instance_property(
313+
prop,
314+
&mut instance_inits,
315+
instance_inits_scope_id,
316+
instance_inits_constructor_scope_id,
317+
ctx,
318+
);
304319
}
305320
}
306321
ClassElement::MethodDefinition(method) => {
@@ -321,7 +336,13 @@ impl<'a> ClassProperties<'a, '_> {
321336
// Create a constructor if there isn't one.
322337
match instance_inits_insert_location {
323338
InstanceInitsInsertLocation::NewConstructor => {
324-
self.insert_constructor(body, instance_inits, has_super_class, ctx);
339+
Self::insert_constructor(
340+
body,
341+
instance_inits,
342+
has_super_class,
343+
instance_inits_scope_id,
344+
ctx,
345+
);
325346
}
326347
InstanceInitsInsertLocation::ExistingConstructor(stmt_index) => {
327348
self.insert_inits_into_constructor_as_statements(
@@ -336,11 +357,17 @@ impl<'a> ClassProperties<'a, '_> {
336357
constructor.as_mut().unwrap(),
337358
instance_inits,
338359
&super_binding,
360+
instance_inits_scope_id,
339361
ctx,
340362
);
341363
}
342364
InstanceInitsInsertLocation::SuperFnOutsideClass(super_binding) => {
343-
self.create_super_function_outside_constructor(instance_inits, &super_binding, ctx);
365+
self.create_super_function_outside_constructor(
366+
instance_inits,
367+
&super_binding,
368+
instance_inits_scope_id,
369+
ctx,
370+
);
344371
}
345372
}
346373
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,18 +215,17 @@ impl<'a> ClassProperties<'a, '_> {
215215

216216
/// Add a constructor to class containing property initializers.
217217
pub(super) fn insert_constructor(
218-
&self,
219218
body: &mut ClassBody<'a>,
220219
inits: Vec<Expression<'a>>,
221220
has_super_class: bool,
221+
constructor_scope_id: ScopeId,
222222
ctx: &mut TraverseCtx<'a>,
223223
) {
224224
// Create statements to go in function body
225225
let mut stmts = ctx.ast.vec_with_capacity(inits.len() + usize::from(has_super_class));
226226

227227
// Add `super(..._args);` statement and `..._args` param if class has a super class.
228228
// `constructor(..._args) { super(..._args); /* prop initialization */ }`
229-
let constructor_scope_id = self.instance_inits_scope_id;
230229
let mut params_rest = None;
231230
if has_super_class {
232231
let args_binding =
@@ -301,6 +300,7 @@ impl<'a> ClassProperties<'a, '_> {
301300
constructor: &mut Function<'a>,
302301
inits: Vec<Expression<'a>>,
303302
super_binding: &BoundIdentifier<'a>,
303+
super_func_scope_id: ScopeId,
304304
ctx: &mut TraverseCtx<'a>,
305305
) {
306306
// Rename any symbols in constructor which clash with references in inits
@@ -313,7 +313,6 @@ impl<'a> ClassProperties<'a, '_> {
313313
// rather than an additional `return this` statement.
314314
// Actually this wouldn't work at present, as `_classPrivateFieldInitSpec(this, _prop, value)`
315315
// does not return `this`. We could alter it so it does when we have our own helper package.
316-
let super_func_scope_id = self.instance_inits_scope_id;
317316
let args_binding =
318317
ctx.generate_uid("args", super_func_scope_id, SymbolFlags::FunctionScopedVariable);
319318
let super_call = create_super_call(&args_binding, ctx);
@@ -371,6 +370,7 @@ impl<'a> ClassProperties<'a, '_> {
371370
&mut self,
372371
inits: Vec<Expression<'a>>,
373372
super_binding: &BoundIdentifier<'a>,
373+
super_func_scope_id: ScopeId,
374374
ctx: &mut TraverseCtx<'a>,
375375
) {
376376
// Add `"use strict"` directive if outer scope is not strict mode
@@ -387,7 +387,6 @@ impl<'a> ClassProperties<'a, '_> {
387387
// `<inits>; return this;`
388388
let body_stmts = ctx.ast.vec_from_iter(exprs_into_stmts(inits, ctx).chain([return_stmt]));
389389
// `function() { <inits>; return this; }`
390-
let super_func_scope_id = self.instance_inits_scope_id;
391390
let super_func = ctx.ast.expression_function_with_scope_id_and_pure(
392391
SPAN,
393392
FunctionType::FunctionExpression,

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,22 @@ impl<'a> ClassProperties<'a, '_> {
2525
pub(super) fn transform_instance_initializer(
2626
&mut self,
2727
value: &Expression<'a>,
28+
instance_inits_scope_id: ScopeId,
29+
instance_inits_constructor_scope_id: Option<ScopeId>,
2830
ctx: &mut TraverseCtx<'a>,
2931
) {
30-
if let Some(constructor_scope_id) = self.instance_inits_constructor_scope_id {
32+
if let Some(constructor_scope_id) = instance_inits_constructor_scope_id {
3133
// Re-parent first-level scopes, and check for symbol clashes
32-
let mut updater = InstanceInitializerVisitor::new(constructor_scope_id, self, ctx);
34+
let mut updater = InstanceInitializerVisitor::new(
35+
instance_inits_scope_id,
36+
constructor_scope_id,
37+
self,
38+
ctx,
39+
);
3340
updater.visit_expression(value);
3441
} else {
3542
// No symbol clashes possible. Just re-parent first-level scopes (faster).
36-
let mut updater = FastInstanceInitializerVisitor::new(self, ctx);
43+
let mut updater = FastInstanceInitializerVisitor::new(instance_inits_scope_id, ctx);
3744
updater.visit_expression(value);
3845
}
3946
}
@@ -58,6 +65,7 @@ struct InstanceInitializerVisitor<'a, 'v> {
5865

5966
impl<'a, 'v> InstanceInitializerVisitor<'a, 'v> {
6067
fn new(
68+
instance_inits_scope_id: ScopeId,
6169
constructor_scope_id: ScopeId,
6270
class_properties: &'v mut ClassProperties<'a, '_>,
6371
ctx: &'v mut TraverseCtx<'a>,
@@ -66,7 +74,7 @@ impl<'a, 'v> InstanceInitializerVisitor<'a, 'v> {
6674
// Most initializers don't contain any scopes, so best default is 0 capacity
6775
// to avoid an allocation in most cases
6876
scope_ids_stack: Stack::new(),
69-
parent_scope_id: class_properties.instance_inits_scope_id,
77+
parent_scope_id: instance_inits_scope_id,
7078
constructor_scope_id,
7179
clashing_symbols: &mut class_properties.clashing_constructor_symbols,
7280
ctx,
@@ -154,8 +162,8 @@ struct FastInstanceInitializerVisitor<'a, 'v> {
154162
}
155163

156164
impl<'a, 'v> FastInstanceInitializerVisitor<'a, 'v> {
157-
fn new(class_properties: &'v ClassProperties<'a, '_>, ctx: &'v mut TraverseCtx<'a>) -> Self {
158-
Self { parent_scope_id: class_properties.instance_inits_scope_id, ctx }
165+
fn new(instance_inits_scope_id: ScopeId, ctx: &'v mut TraverseCtx<'a>) -> Self {
166+
Self { parent_scope_id: instance_inits_scope_id, ctx }
159167
}
160168
}
161169

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

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ use serde::Deserialize;
203203

204204
use oxc_ast::ast::*;
205205
use oxc_span::Atom;
206-
use oxc_syntax::{scope::ScopeId, symbol::SymbolId};
206+
use oxc_syntax::symbol::SymbolId;
207207
use oxc_traverse::{Traverse, TraverseCtx};
208208

209209
use crate::TransformCtx;
@@ -266,15 +266,6 @@ pub struct ClassProperties<'a, 'ctx> {
266266

267267
// ----- State used only during enter phase -----
268268
//
269-
/// Scope that instance property initializers will be inserted into.
270-
/// This is usually class constructor, but can also be a `_super` function which is created.
271-
instance_inits_scope_id: ScopeId,
272-
/// Scope of class constructor, if instance property initializers will be inserted into constructor.
273-
/// Used for checking for variable name clashes.
274-
/// e.g. `class C { prop = x(); constructor(x) {} }`
275-
/// - `x` in constructor needs to be renamed when `x()` is moved into constructor body.
276-
/// `None` if class has no existing constructor, as then there can't be any clashes.
277-
instance_inits_constructor_scope_id: Option<ScopeId>,
278269
/// Symbols in constructor which clash with instance prop initializers.
279270
/// Keys are symbols' IDs.
280271
/// Values are initially the original name of binding, later on the name of new UID name.
@@ -310,9 +301,6 @@ impl<'a, 'ctx> ClassProperties<'a, 'ctx> {
310301
ctx,
311302
classes_stack: ClassesStack::new(),
312303
private_field_count: 0,
313-
// Temporary values - overwritten when entering class
314-
instance_inits_scope_id: ScopeId::new(0),
315-
instance_inits_constructor_scope_id: None,
316304
// `Vec`s and `FxHashMap`s which are reused for every class being transformed
317305
clashing_constructor_symbols: FxHashMap::default(),
318306
insert_before: vec![],

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! Transform of class property declarations (instance or static properties).
33
44
use oxc_ast::{NONE, ast::*};
5+
use oxc_semantic::ScopeId;
56
use oxc_span::SPAN;
67
use oxc_syntax::reference::ReferenceFlags;
78
use oxc_traverse::TraverseCtx;
@@ -21,12 +22,19 @@ impl<'a> ClassProperties<'a, '_> {
2122
&mut self,
2223
prop: &mut PropertyDefinition<'a>,
2324
instance_inits: &mut Vec<Expression<'a>>,
25+
instance_inits_scope_id: ScopeId,
26+
instance_inits_constructor_scope_id: Option<ScopeId>,
2427
ctx: &mut TraverseCtx<'a>,
2528
) {
2629
// Get value
2730
let value = match prop.value.take() {
2831
Some(value) => {
29-
self.transform_instance_initializer(&value, ctx);
32+
self.transform_instance_initializer(
33+
&value,
34+
instance_inits_scope_id,
35+
instance_inits_constructor_scope_id,
36+
ctx,
37+
);
3038
value
3139
}
3240
None => ctx.ast.void_0(SPAN),

0 commit comments

Comments
 (0)