@@ -3,21 +3,68 @@ use oxc_ast::{AstKind, ast::*};
3
3
use oxc_semantic:: { AstNode , AstNodes , ReferenceId , Scoping , SymbolId } ;
4
4
use rustc_hash:: FxHashSet ;
5
5
6
- #[ cfg_attr( not( test) , expect( dead_code) ) ]
7
- pub fn collect_name_symbols ( scoping : & Scoping , ast_nodes : & AstNodes ) -> FxHashSet < SymbolId > {
8
- let collector = NameSymbolCollector :: new ( scoping, ast_nodes) ;
6
+ #[ derive( Debug , Clone , Copy , Default ) ]
7
+ pub struct MangleOptionsKeepNames {
8
+ /// Keep function names so that `Function.prototype.name` is preserved.
9
+ ///
10
+ /// Default `false`
11
+ pub function : bool ,
12
+
13
+ /// Keep class names so that `Class.prototype.name` is preserved.
14
+ ///
15
+ /// Default `false`
16
+ pub class : bool ,
17
+ }
18
+
19
+ impl MangleOptionsKeepNames {
20
+ pub fn all_false ( ) -> Self {
21
+ Self { function : false , class : false }
22
+ }
23
+
24
+ pub fn all_true ( ) -> Self {
25
+ Self { function : true , class : true }
26
+ }
27
+
28
+ #[ cfg( test) ]
29
+ pub ( crate ) fn function_only ( ) -> Self {
30
+ Self { function : true , class : false }
31
+ }
32
+
33
+ #[ cfg( test) ]
34
+ pub ( crate ) fn class_only ( ) -> Self {
35
+ Self { function : false , class : true }
36
+ }
37
+ }
38
+
39
+ impl From < bool > for MangleOptionsKeepNames {
40
+ fn from ( keep_names : bool ) -> Self {
41
+ if keep_names { Self :: all_true ( ) } else { Self :: all_false ( ) }
42
+ }
43
+ }
44
+
45
+ pub fn collect_name_symbols (
46
+ options : MangleOptionsKeepNames ,
47
+ scoping : & Scoping ,
48
+ ast_nodes : & AstNodes ,
49
+ ) -> FxHashSet < SymbolId > {
50
+ let collector = NameSymbolCollector :: new ( options, scoping, ast_nodes) ;
9
51
collector. collect ( )
10
52
}
11
53
12
54
/// Collects symbols that are used to set `name` properties of functions and classes.
13
55
struct NameSymbolCollector < ' a , ' b > {
56
+ options : MangleOptionsKeepNames ,
14
57
scoping : & ' b Scoping ,
15
58
ast_nodes : & ' b AstNodes < ' a > ,
16
59
}
17
60
18
61
impl < ' a , ' b : ' a > NameSymbolCollector < ' a , ' b > {
19
- fn new ( scoping : & ' b Scoping , ast_nodes : & ' b AstNodes < ' a > ) -> Self {
20
- Self { scoping, ast_nodes }
62
+ fn new (
63
+ options : MangleOptionsKeepNames ,
64
+ scoping : & ' b Scoping ,
65
+ ast_nodes : & ' b AstNodes < ' a > ,
66
+ ) -> Self {
67
+ Self { options, scoping, ast_nodes }
21
68
}
22
69
23
70
fn collect ( self ) -> FxHashSet < SymbolId > {
@@ -42,9 +89,12 @@ impl<'a, 'b: 'a> NameSymbolCollector<'a, 'b> {
42
89
fn is_name_set_declare_node ( & self , node : & ' a AstNode , symbol_id : SymbolId ) -> bool {
43
90
match node. kind ( ) {
44
91
AstKind :: Function ( function) => {
45
- function. id . as_ref ( ) . is_some_and ( |id| id. symbol_id ( ) == symbol_id)
92
+ self . options . function
93
+ && function. id . as_ref ( ) . is_some_and ( |id| id. symbol_id ( ) == symbol_id)
94
+ }
95
+ AstKind :: Class ( cls) => {
96
+ self . options . class && cls. id . as_ref ( ) . is_some_and ( |id| id. symbol_id ( ) == symbol_id)
46
97
}
47
- AstKind :: Class ( cls) => cls. id . as_ref ( ) . is_some_and ( |id| id. symbol_id ( ) == symbol_id) ,
48
98
AstKind :: VariableDeclarator ( decl) => {
49
99
if let BindingPatternKind :: BindingIdentifier ( id) = & decl. id . kind {
50
100
if id. symbol_id ( ) == symbol_id {
@@ -176,9 +226,18 @@ impl<'a, 'b: 'a> NameSymbolCollector<'a, 'b> {
176
226
}
177
227
}
178
228
179
- #[ expect( clippy:: unused_self) ]
180
229
fn is_expression_whose_name_needs_to_be_kept ( & self , expr : & Expression ) -> bool {
181
- expr. is_anonymous_function_definition ( )
230
+ let is_anonymous = expr. is_anonymous_function_definition ( ) ;
231
+ if !is_anonymous {
232
+ return false ;
233
+ }
234
+
235
+ if self . options . class && self . options . function {
236
+ return true ;
237
+ }
238
+
239
+ let is_class = matches ! ( expr, Expression :: ClassExpression ( _) ) ;
240
+ ( self . options . class && is_class) || ( self . options . function && !is_class)
182
241
}
183
242
}
184
243
@@ -191,17 +250,17 @@ mod test {
191
250
use rustc_hash:: FxHashSet ;
192
251
use std:: iter:: once;
193
252
194
- use super :: collect_name_symbols;
253
+ use super :: { MangleOptionsKeepNames , collect_name_symbols} ;
195
254
196
- fn collect ( source_text : & str ) -> FxHashSet < String > {
255
+ fn collect ( opts : MangleOptionsKeepNames , source_text : & str ) -> FxHashSet < String > {
197
256
let allocator = Allocator :: default ( ) ;
198
257
let ret = Parser :: new ( & allocator, source_text, SourceType :: mjs ( ) ) . parse ( ) ;
199
258
assert ! ( !ret. panicked, "{source_text}" ) ;
200
259
assert ! ( ret. errors. is_empty( ) , "{source_text}" ) ;
201
260
let ret = SemanticBuilder :: new ( ) . build ( & ret. program ) ;
202
261
assert ! ( ret. errors. is_empty( ) , "{source_text}" ) ;
203
262
let semantic = ret. semantic ;
204
- let symbols = collect_name_symbols ( semantic. scoping ( ) , semantic. nodes ( ) ) ;
263
+ let symbols = collect_name_symbols ( opts , semantic. scoping ( ) , semantic. nodes ( ) ) ;
205
264
symbols
206
265
. into_iter ( )
207
266
. map ( |symbol_id| semantic. scoping ( ) . symbol_name ( symbol_id) . to_string ( ) )
@@ -210,60 +269,120 @@ mod test {
210
269
211
270
#[ test]
212
271
fn test_declarations ( ) {
213
- assert_eq ! ( collect( "function foo() {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
214
- assert_eq ! ( collect( "class Foo {}" ) , once( "Foo" . to_string( ) ) . collect( ) ) ;
272
+ assert_eq ! (
273
+ collect( MangleOptionsKeepNames :: function_only( ) , "function foo() {}" ) ,
274
+ once( "foo" . to_string( ) ) . collect( )
275
+ ) ;
276
+ assert_eq ! (
277
+ collect( MangleOptionsKeepNames :: class_only( ) , "class Foo {}" ) ,
278
+ once( "Foo" . to_string( ) ) . collect( )
279
+ ) ;
215
280
}
216
281
217
282
#[ test]
218
283
fn test_simple_declare_init ( ) {
219
- assert_eq ! ( collect( "var foo = function() {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
220
- assert_eq ! ( collect( "var foo = () => {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
221
- assert_eq ! ( collect( "var Foo = class {}" ) , once( "Foo" . to_string( ) ) . collect( ) ) ;
284
+ assert_eq ! (
285
+ collect( MangleOptionsKeepNames :: function_only( ) , "var foo = function() {}" ) ,
286
+ once( "foo" . to_string( ) ) . collect( )
287
+ ) ;
288
+ assert_eq ! (
289
+ collect( MangleOptionsKeepNames :: function_only( ) , "var foo = () => {}" ) ,
290
+ once( "foo" . to_string( ) ) . collect( )
291
+ ) ;
292
+ assert_eq ! (
293
+ collect( MangleOptionsKeepNames :: class_only( ) , "var Foo = class {}" ) ,
294
+ once( "Foo" . to_string( ) ) . collect( )
295
+ ) ;
222
296
}
223
297
224
298
#[ test]
225
299
fn test_simple_assign ( ) {
226
- assert_eq ! ( collect( "var foo; foo = function() {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
227
- assert_eq ! ( collect( "var foo; foo = () => {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
228
- assert_eq ! ( collect( "var Foo; Foo = class {}" ) , once( "Foo" . to_string( ) ) . collect( ) ) ;
300
+ assert_eq ! (
301
+ collect( MangleOptionsKeepNames :: function_only( ) , "var foo; foo = function() {}" ) ,
302
+ once( "foo" . to_string( ) ) . collect( )
303
+ ) ;
304
+ assert_eq ! (
305
+ collect( MangleOptionsKeepNames :: function_only( ) , "var foo; foo = () => {}" ) ,
306
+ once( "foo" . to_string( ) ) . collect( )
307
+ ) ;
308
+ assert_eq ! (
309
+ collect( MangleOptionsKeepNames :: class_only( ) , "var Foo; Foo = class {}" ) ,
310
+ once( "Foo" . to_string( ) ) . collect( )
311
+ ) ;
229
312
230
- assert_eq ! ( collect( "var foo; foo ||= function() {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
231
313
assert_eq ! (
232
- collect( "var foo = 1; foo &&= function() {}" ) ,
314
+ collect( MangleOptionsKeepNames :: function_only( ) , "var foo; foo ||= function() {}" ) ,
315
+ once( "foo" . to_string( ) ) . collect( )
316
+ ) ;
317
+ assert_eq ! (
318
+ collect( MangleOptionsKeepNames :: function_only( ) , "var foo = 1; foo &&= function() {}" ) ,
319
+ once( "foo" . to_string( ) ) . collect( )
320
+ ) ;
321
+ assert_eq ! (
322
+ collect( MangleOptionsKeepNames :: function_only( ) , "var foo; foo ??= function() {}" ) ,
233
323
once( "foo" . to_string( ) ) . collect( )
234
324
) ;
235
- assert_eq ! ( collect( "var foo; foo ??= function() {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
236
325
}
237
326
238
327
#[ test]
239
328
fn test_default_declarations ( ) {
240
- assert_eq ! ( collect( "var [foo = function() {}] = []" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
241
- assert_eq ! ( collect( "var [foo = () => {}] = []" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
242
- assert_eq ! ( collect( "var [Foo = class {}] = []" ) , once( "Foo" . to_string( ) ) . collect( ) ) ;
243
- assert_eq ! ( collect( "var { foo = function() {} } = {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
329
+ assert_eq ! (
330
+ collect( MangleOptionsKeepNames :: function_only( ) , "var [foo = function() {}] = []" ) ,
331
+ once( "foo" . to_string( ) ) . collect( )
332
+ ) ;
333
+ assert_eq ! (
334
+ collect( MangleOptionsKeepNames :: function_only( ) , "var [foo = () => {}] = []" ) ,
335
+ once( "foo" . to_string( ) ) . collect( )
336
+ ) ;
337
+ assert_eq ! (
338
+ collect( MangleOptionsKeepNames :: class_only( ) , "var [Foo = class {}] = []" ) ,
339
+ once( "Foo" . to_string( ) ) . collect( )
340
+ ) ;
341
+ assert_eq ! (
342
+ collect( MangleOptionsKeepNames :: function_only( ) , "var { foo = function() {} } = {}" ) ,
343
+ once( "foo" . to_string( ) ) . collect( )
344
+ ) ;
244
345
}
245
346
246
347
#[ test]
247
348
fn test_default_assign ( ) {
248
349
assert_eq ! (
249
- collect( "var foo; [foo = function() {}] = []" ) ,
350
+ collect( MangleOptionsKeepNames :: function_only ( ) , "var foo; [foo = function() {}] = []" ) ,
250
351
once( "foo" . to_string( ) ) . collect( )
251
352
) ;
252
- assert_eq ! ( collect( "var foo; [foo = () => {}] = []" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
253
- assert_eq ! ( collect( "var Foo; [Foo = class {}] = []" ) , once( "Foo" . to_string( ) ) . collect( ) ) ;
254
353
assert_eq ! (
255
- collect( "var foo; ({ foo = function() {} } = {})" ) ,
354
+ collect( MangleOptionsKeepNames :: function_only( ) , "var foo; [foo = () => {}] = []" ) ,
355
+ once( "foo" . to_string( ) ) . collect( )
356
+ ) ;
357
+ assert_eq ! (
358
+ collect( MangleOptionsKeepNames :: class_only( ) , "var Foo; [Foo = class {}] = []" ) ,
359
+ once( "Foo" . to_string( ) ) . collect( )
360
+ ) ;
361
+ assert_eq ! (
362
+ collect(
363
+ MangleOptionsKeepNames :: function_only( ) ,
364
+ "var foo; ({ foo = function() {} } = {})"
365
+ ) ,
256
366
once( "foo" . to_string( ) ) . collect( )
257
367
) ;
258
368
}
259
369
260
370
#[ test]
261
371
fn test_for_in_declaration ( ) {
262
372
assert_eq ! (
263
- collect( "for (var foo = function() {} in []) {}" ) ,
373
+ collect(
374
+ MangleOptionsKeepNames :: function_only( ) ,
375
+ "for (var foo = function() {} in []) {}"
376
+ ) ,
377
+ once( "foo" . to_string( ) ) . collect( )
378
+ ) ;
379
+ assert_eq ! (
380
+ collect( MangleOptionsKeepNames :: function_only( ) , "for (var foo = () => {} in []) {}" ) ,
264
381
once( "foo" . to_string( ) ) . collect( )
265
382
) ;
266
- assert_eq ! ( collect( "for (var foo = () => {} in []) {}" ) , once( "foo" . to_string( ) ) . collect( ) ) ;
267
- assert_eq ! ( collect( "for (var Foo = class {} in []) {}" ) , once( "Foo" . to_string( ) ) . collect( ) ) ;
383
+ assert_eq ! (
384
+ collect( MangleOptionsKeepNames :: class_only( ) , "for (var Foo = class {} in []) {}" ) ,
385
+ once( "Foo" . to_string( ) ) . collect( )
386
+ ) ;
268
387
}
269
388
}
0 commit comments