1use itoa::Buffer as ItoaBuffer;
43
44use oxc_allocator::TakeIn;
45use oxc_ast::{NONE, ast::*};
46use oxc_span::SPAN;
47use oxc_syntax::scope::{ScopeFlags, ScopeId};
48use oxc_traverse::Traverse;
49
50use crate::{
51 context::TraverseCtx, state::TransformState,
52 utils::ast_builder::wrap_statements_in_arrow_function_iife,
53};
54
55pub struct ClassStaticBlock;
56
57impl ClassStaticBlock {
58 pub fn new() -> Self {
59 Self
60 }
61}
62
63impl<'a> Traverse<'a, TransformState<'a>> for ClassStaticBlock {
64 fn enter_class_body(&mut self, body: &mut ClassBody<'a>, ctx: &mut TraverseCtx<'a>) {
65 let mut has_static_block = false;
72 let mut keys = Keys::default();
73 for element in &body.body {
74 let key = match element {
75 ClassElement::StaticBlock(_) => {
76 has_static_block = true;
77 continue;
78 }
79 ClassElement::MethodDefinition(def) => &def.key,
80 ClassElement::PropertyDefinition(def) => &def.key,
81 ClassElement::AccessorProperty(def) => &def.key,
82 ClassElement::TSIndexSignature(_) => continue,
83 };
84
85 if let PropertyKey::PrivateIdentifier(id) = key {
86 keys.reserve(id.name.as_str());
87 }
88 }
89
90 if !has_static_block {
92 return;
93 }
94
95 for element in &mut body.body {
96 if let ClassElement::StaticBlock(block) = element {
97 *element = Self::convert_block_to_private_field(block, &mut keys, ctx);
98 }
99 }
100 }
101}
102
103impl ClassStaticBlock {
104 fn convert_block_to_private_field<'a>(
108 block: &mut StaticBlock<'a>,
109 keys: &mut Keys<'a>,
110 ctx: &mut TraverseCtx<'a>,
111 ) -> ClassElement<'a> {
112 let expr = Self::convert_block_to_expression(block, ctx);
113
114 let key = keys.get_unique(ctx);
115 let key = ctx.ast.property_key_private_identifier(SPAN, key);
116
117 ctx.ast.class_element_property_definition(
118 block.span,
119 PropertyDefinitionType::PropertyDefinition,
120 ctx.ast.vec(),
121 key,
122 NONE,
123 Some(expr),
124 false,
125 true,
126 false,
127 false,
128 false,
129 false,
130 false,
131 None,
132 )
133 }
134
135 fn convert_block_to_expression<'a>(
139 block: &mut StaticBlock<'a>,
140 ctx: &mut TraverseCtx<'a>,
141 ) -> Expression<'a> {
142 let scope_id = block.scope_id();
143
144 let stmts = &mut block.body;
148 if stmts.len() == 1
149 && let Statement::ExpressionStatement(stmt) = stmts.first_mut().unwrap()
150 {
151 return Self::convert_block_with_single_expression_to_expression(
152 &mut stmt.expression,
153 scope_id,
154 ctx,
155 );
156 }
157
158 *ctx.scoping_mut().scope_flags_mut(scope_id) =
164 ScopeFlags::Function | ScopeFlags::Arrow | ScopeFlags::StrictMode;
165 wrap_statements_in_arrow_function_iife(stmts.take_in(ctx.ast), scope_id, block.span, ctx)
166 }
167
168 fn convert_block_with_single_expression_to_expression<'a>(
172 expr: &mut Expression<'a>,
173 scope_id: ScopeId,
174 ctx: &mut TraverseCtx<'a>,
175 ) -> Expression<'a> {
176 let expr = expr.take_in(ctx.ast);
177
178 ctx.remove_scope_for_expression(scope_id, &expr);
180
181 expr
182 }
183}
184
185#[derive(Default)]
198struct Keys<'a> {
199 underscore: bool,
201 numbered: Vec<&'a str>,
203}
204
205impl<'a> Keys<'a> {
206 fn reserve(&mut self, key: &'a str) {
210 let mut bytes = key.as_bytes().iter().copied();
211 if bytes.next() != Some(b'_') {
212 return;
213 }
214
215 match bytes.next() {
216 None => {
217 self.underscore = true;
218 }
219 Some(b'1'..=b'9') => {
220 self.numbered.push(&key[1..]);
221 }
222 _ => {}
223 }
224 }
225
226 #[inline]
230 fn get_unique(&mut self, ctx: &TraverseCtx<'a>) -> Str<'a> {
231 #[expect(clippy::if_not_else)]
232 if !self.underscore {
233 self.underscore = true;
234 Str::from("_")
235 } else {
236 self.get_unique_slow(ctx)
237 }
238 }
239
240 #[cold]
242 #[inline(never)]
243 fn get_unique_slow(&mut self, ctx: &TraverseCtx<'a>) -> Str<'a> {
244 let mut i = 2u32;
247 let mut buffer = ItoaBuffer::new();
248 let mut num_str;
249 loop {
250 num_str = buffer.format(i);
251 if !self.numbered.contains(&num_str) {
252 break;
253 }
254 i += 1;
255 }
256
257 let key = ctx.ast.str_from_strs_array(["_", num_str]);
258 self.numbered.push(&key.as_str()[1..]);
259
260 key
261 }
262}
263
264#[cfg(test)]
265mod test {
266 use oxc_allocator::Allocator;
267 use oxc_semantic::Scoping;
268 use oxc_traverse::ReusableTraverseCtx;
269
270 use crate::state::TransformState;
271
272 use super::Keys;
273
274 macro_rules! setup {
275 ($ctx:ident) => {
276 let allocator = Allocator::default();
277 let scoping = Scoping::default();
278 let state = TransformState::default();
279 let ctx = ReusableTraverseCtx::new(state, scoping, &allocator);
280 let mut ctx = unsafe { ctx.unwrap() };
282 let $ctx = &mut ctx;
283 };
284 }
285
286 #[test]
287 fn keys_no_reserved() {
288 setup!(ctx);
289
290 let mut keys = Keys::default();
291
292 assert_eq!(keys.get_unique(ctx), "_");
293 assert_eq!(keys.get_unique(ctx), "_2");
294 assert_eq!(keys.get_unique(ctx), "_3");
295 assert_eq!(keys.get_unique(ctx), "_4");
296 assert_eq!(keys.get_unique(ctx), "_5");
297 assert_eq!(keys.get_unique(ctx), "_6");
298 assert_eq!(keys.get_unique(ctx), "_7");
299 assert_eq!(keys.get_unique(ctx), "_8");
300 assert_eq!(keys.get_unique(ctx), "_9");
301 assert_eq!(keys.get_unique(ctx), "_10");
302 assert_eq!(keys.get_unique(ctx), "_11");
303 assert_eq!(keys.get_unique(ctx), "_12");
304 }
305
306 #[test]
307 fn keys_no_relevant_reserved() {
308 setup!(ctx);
309
310 let mut keys = Keys::default();
311 keys.reserve("a");
312 keys.reserve("foo");
313 keys.reserve("__");
314 keys.reserve("_0");
315 keys.reserve("_1");
316 keys.reserve("_a");
317 keys.reserve("_foo");
318 keys.reserve("_2foo");
319
320 assert_eq!(keys.get_unique(ctx), "_");
321 assert_eq!(keys.get_unique(ctx), "_2");
322 assert_eq!(keys.get_unique(ctx), "_3");
323 }
324
325 #[test]
326 fn keys_reserved_underscore() {
327 setup!(ctx);
328
329 let mut keys = Keys::default();
330 keys.reserve("_");
331
332 assert_eq!(keys.get_unique(ctx), "_2");
333 assert_eq!(keys.get_unique(ctx), "_3");
334 assert_eq!(keys.get_unique(ctx), "_4");
335 }
336
337 #[test]
338 fn keys_reserved_numbers() {
339 setup!(ctx);
340
341 let mut keys = Keys::default();
342 keys.reserve("_2");
343 keys.reserve("_4");
344 keys.reserve("_11");
345
346 assert_eq!(keys.get_unique(ctx), "_");
347 assert_eq!(keys.get_unique(ctx), "_3");
348 assert_eq!(keys.get_unique(ctx), "_5");
349 assert_eq!(keys.get_unique(ctx), "_6");
350 assert_eq!(keys.get_unique(ctx), "_7");
351 assert_eq!(keys.get_unique(ctx), "_8");
352 assert_eq!(keys.get_unique(ctx), "_9");
353 assert_eq!(keys.get_unique(ctx), "_10");
354 assert_eq!(keys.get_unique(ctx), "_12");
355 }
356
357 #[test]
358 fn keys_reserved_later_numbers() {
359 setup!(ctx);
360
361 let mut keys = Keys::default();
362 keys.reserve("_5");
363 keys.reserve("_4");
364 keys.reserve("_12");
365 keys.reserve("_13");
366
367 assert_eq!(keys.get_unique(ctx), "_");
368 assert_eq!(keys.get_unique(ctx), "_2");
369 assert_eq!(keys.get_unique(ctx), "_3");
370 assert_eq!(keys.get_unique(ctx), "_6");
371 assert_eq!(keys.get_unique(ctx), "_7");
372 assert_eq!(keys.get_unique(ctx), "_8");
373 assert_eq!(keys.get_unique(ctx), "_9");
374 assert_eq!(keys.get_unique(ctx), "_10");
375 assert_eq!(keys.get_unique(ctx), "_11");
376 assert_eq!(keys.get_unique(ctx), "_14");
377 }
378
379 #[test]
380 fn keys_reserved_underscore_and_numbers() {
381 setup!(ctx);
382
383 let mut keys = Keys::default();
384 keys.reserve("_2");
385 keys.reserve("_4");
386 keys.reserve("_");
387
388 assert_eq!(keys.get_unique(ctx), "_3");
389 assert_eq!(keys.get_unique(ctx), "_5");
390 assert_eq!(keys.get_unique(ctx), "_6");
391 }
392
393 #[test]
394 fn keys_reserved_underscore_and_later_numbers() {
395 setup!(ctx);
396
397 let mut keys = Keys::default();
398 keys.reserve("_5");
399 keys.reserve("_4");
400 keys.reserve("_");
401
402 assert_eq!(keys.get_unique(ctx), "_2");
403 assert_eq!(keys.get_unique(ctx), "_3");
404 assert_eq!(keys.get_unique(ctx), "_6");
405 }
406}