@@ -119,7 +119,7 @@ pub enum ArrowFunctionConverterMode {
119
119
AsyncOnly ,
120
120
}
121
121
122
- #[ derive( PartialEq , Eq , Hash ) ]
122
+ #[ derive( Debug , PartialEq , Eq , Hash ) ]
123
123
struct SuperMethodKey < ' a > {
124
124
/// If it is true, the method should accept a value parameter.
125
125
is_assignment : bool ,
@@ -144,7 +144,8 @@ pub struct ArrowFunctionConverter<'a> {
144
144
renamed_arguments_symbol_ids : FxHashSet < SymbolId > ,
145
145
// TODO(improve-on-babel): `FxHashMap` would suffice here. Iteration order is not important.
146
146
// Only using `FxIndexMap` for predictable iteration order to match Babel's output.
147
- super_methods_stack : SparseStack < FxIndexMap < SuperMethodKey < ' a > , SuperMethodInfo < ' a > > > ,
147
+ super_methods_stack : NonEmptyStack < FxIndexMap < SuperMethodKey < ' a > , SuperMethodInfo < ' a > > > ,
148
+ super_needs_transform_stack : NonEmptyStack < bool > ,
148
149
}
149
150
150
151
impl ArrowFunctionConverter < ' _ > {
@@ -164,7 +165,8 @@ impl ArrowFunctionConverter<'_> {
164
165
constructor_super_stack : NonEmptyStack :: new ( false ) ,
165
166
arguments_needs_transform_stack : NonEmptyStack :: new ( false ) ,
166
167
renamed_arguments_symbol_ids : FxHashSet :: default ( ) ,
167
- super_methods_stack : SparseStack :: new ( ) ,
168
+ super_methods_stack : NonEmptyStack :: new ( FxIndexMap :: default ( ) ) ,
169
+ super_needs_transform_stack : NonEmptyStack :: new ( false ) ,
168
170
}
169
171
}
170
172
}
@@ -190,18 +192,19 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
190
192
None ,
191
193
ctx,
192
194
) ;
193
-
194
195
debug_assert ! ( self . this_var_stack. len( ) == 1 ) ;
195
196
debug_assert ! ( self . this_var_stack. last( ) . is_none( ) ) ;
196
197
debug_assert ! ( self . arguments_var_stack. len( ) == 1 ) ;
197
198
debug_assert ! ( self . arguments_var_stack. last( ) . is_none( ) ) ;
198
199
debug_assert ! ( self . constructor_super_stack. len( ) == 1 ) ;
199
200
// TODO: This assertion currently failing because we don't handle `super` in arrow functions
200
201
// in class static properties correctly.
201
- // e.g. `class C { static f = async () => super.prop; }`
202
+ // e.g. `class C { static f = () => super.prop; }`
202
203
// debug_assert!(self.constructor_super_stack.last() == &false);
203
204
debug_assert ! ( self . super_methods_stack. len( ) == 1 ) ;
204
- debug_assert ! ( self . super_methods_stack. last( ) . is_none( ) ) ;
205
+ debug_assert ! ( self . super_methods_stack. last( ) . is_empty( ) ) ;
206
+ debug_assert ! ( self . super_needs_transform_stack. len( ) == 1 ) ;
207
+ debug_assert ! ( self . super_needs_transform_stack. last( ) == & false ) ;
205
208
}
206
209
207
210
fn enter_function ( & mut self , func : & mut Function < ' a > , ctx : & mut TraverseCtx < ' a > ) {
@@ -213,32 +216,9 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
213
216
self . arguments_var_stack . push ( None ) ;
214
217
self . constructor_super_stack . push ( false ) ;
215
218
216
- if self . is_async_only ( )
217
- && ( func. r#async || self . super_methods_stack . len ( ) > 1 )
218
- && Self :: is_class_method_like_ancestor ( ctx. parent ( ) )
219
- {
220
- // `self.super_methods_stack.len() > 1` means we are in a nested class method
221
- //
222
- // Only `super` that inside async methods need to be transformed, if it is a
223
- // nested class method and it is not async, we still need to push a `None` to
224
- // `self.super_methods_stack`, because if we don't get a `FxIndexMap` from
225
- // `self.super_methods_stack.last_mut()`, that means we don't need to transform.
226
- // See how to transform `super` in `self.transform_member_expression_for_super`
227
- //
228
- // ```js
229
- // class Outer {
230
- // async method() {
231
- // class Inner extends Outer {
232
- // normal() {
233
- // // `super.value` should not be transformed, because it is not in an async method
234
- // super.value
235
- // }
236
- // }
237
- // }
238
- // }
239
- // ```
240
- let super_methods = if func. r#async { Some ( FxIndexMap :: default ( ) ) } else { None } ;
241
- self . super_methods_stack . push ( super_methods) ;
219
+ if Self :: is_class_method_like_ancestor ( ctx. parent ( ) ) {
220
+ self . super_methods_stack . push ( FxIndexMap :: default ( ) ) ;
221
+ self . super_needs_transform_stack . push ( func. r#async ) ;
242
222
}
243
223
}
244
224
@@ -264,14 +244,11 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
264
244
} ;
265
245
let this_var = self . this_var_stack . pop ( ) ;
266
246
let arguments_var = self . arguments_var_stack . pop ( ) ;
267
- let super_methods = if self . is_async_only ( )
268
- && ( func. r#async || self . super_methods_stack . len ( ) > 1 )
269
- && Self :: is_class_method_like_ancestor ( ctx. parent ( ) )
270
- {
247
+ let super_methods = Self :: is_class_method_like_ancestor ( ctx. parent ( ) ) . then ( || {
248
+ self . super_needs_transform_stack . pop ( ) ;
271
249
self . super_methods_stack . pop ( )
272
- } else {
273
- None
274
- } ;
250
+ } ) ;
251
+
275
252
self . insert_variable_statement_at_the_top_of_statements (
276
253
scope_id,
277
254
& mut body. statements ,
@@ -286,11 +263,41 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
286
263
fn enter_arrow_function_expression (
287
264
& mut self ,
288
265
arrow : & mut ArrowFunctionExpression < ' a > ,
289
- _ctx : & mut TraverseCtx < ' a > ,
266
+ ctx : & mut TraverseCtx < ' a > ,
290
267
) {
291
268
if self . is_async_only ( ) {
292
269
let previous = * self . arguments_needs_transform_stack . last ( ) ;
293
270
self . arguments_needs_transform_stack . push ( previous || arrow. r#async ) ;
271
+
272
+ if Self :: if_ancestor_of_class_property_definition_value ( ctx) {
273
+ self . this_var_stack . push ( None ) ;
274
+ self . super_methods_stack . push ( FxIndexMap :: default ( ) ) ;
275
+ }
276
+ self . super_needs_transform_stack
277
+ . push ( arrow. r#async || * self . super_needs_transform_stack . last ( ) ) ;
278
+ }
279
+ }
280
+
281
+ fn exit_arrow_function_expression (
282
+ & mut self ,
283
+ arrow : & mut ArrowFunctionExpression < ' a > ,
284
+ ctx : & mut TraverseCtx < ' a > ,
285
+ ) {
286
+ if self . is_async_only ( ) {
287
+ if Self :: if_ancestor_of_class_property_definition_value ( ctx) {
288
+ let this_var = self . this_var_stack . pop ( ) ;
289
+ let super_methods = self . super_methods_stack . pop ( ) ;
290
+ self . insert_variable_statement_at_the_top_of_statements (
291
+ arrow. scope_id ( ) ,
292
+ & mut arrow. body . statements ,
293
+ this_var,
294
+ None ,
295
+ Some ( super_methods) ,
296
+ ctx,
297
+ ) ;
298
+ }
299
+
300
+ self . super_needs_transform_stack . pop ( ) ;
294
301
}
295
302
}
296
303
@@ -318,6 +325,7 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
318
325
}
319
326
320
327
self . this_var_stack . push ( None ) ;
328
+ self . super_methods_stack . push ( FxIndexMap :: default ( ) ) ;
321
329
}
322
330
323
331
fn exit_static_block ( & mut self , block : & mut StaticBlock < ' a > , ctx : & mut TraverseCtx < ' a > ) {
@@ -326,14 +334,14 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
326
334
}
327
335
328
336
let this_var = self . this_var_stack . pop ( ) ;
337
+ let super_methods = self . super_methods_stack . pop ( ) ;
329
338
self . insert_variable_statement_at_the_top_of_statements (
330
339
block. scope_id ( ) ,
331
340
& mut block. body ,
332
341
this_var,
333
342
// `arguments` is not allowed to be used in static blocks
334
343
None ,
335
- // `super()` Only allowed in class constructor
336
- None ,
344
+ Some ( super_methods) ,
337
345
ctx,
338
346
) ;
339
347
}
@@ -390,6 +398,36 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
390
398
match_member_expression ! ( Expression ) => {
391
399
self . transform_member_expression_for_super ( expr, None , ctx)
392
400
}
401
+ Expression :: ArrowFunctionExpression ( arrow) => {
402
+ // TODO: If the async arrow function without `this` or `super` usage, we can skip this step.
403
+ if self . is_async_only ( )
404
+ && arrow. r#async
405
+ && Self :: if_ancestor_of_class_property_definition_value ( ctx)
406
+ {
407
+ // Inside class property definition value, since async arrow function will be
408
+ // converted to a generator function by `AsyncToGenerator` plugin, ensure
409
+ // `_this = this` and `super` methods are inserted correctly. We need to
410
+ // wrap the async arrow function with an normal arrow function IIFE.
411
+ //
412
+ // ```js
413
+ // class A {
414
+ // prop = async () => {}
415
+ // }
416
+ // // to
417
+ // class A {
418
+ // prop = (() => { return async () => {} })();
419
+ // }
420
+ // ```
421
+ Some ( Self :: wrap_arrow_function_with_iife (
422
+ arrow. span ,
423
+ arrow. scope_id ( ) ,
424
+ expr,
425
+ ctx,
426
+ ) )
427
+ } else {
428
+ return ;
429
+ }
430
+ }
393
431
_ => return ,
394
432
} ;
395
433
@@ -404,7 +442,9 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
404
442
}
405
443
406
444
if let Expression :: ArrowFunctionExpression ( arrow_function_expr) = expr {
407
- if self . is_async_only ( ) && !arrow_function_expr. r#async {
445
+ // TODO: Here should return early as long as the async-to-generator plugin is enabled,
446
+ // but currently we don't know which plugin is enabled.
447
+ if self . is_async_only ( ) || arrow_function_expr. r#async {
408
448
return ;
409
449
}
410
450
@@ -640,6 +680,19 @@ impl<'a> ArrowFunctionConverter<'a> {
640
680
}
641
681
}
642
682
683
+ /// Check whether the ancestor is an [`Ancestor::PropertyDefinitionValue`],
684
+ /// return false if it's reached the statement.
685
+ fn if_ancestor_of_class_property_definition_value ( ctx : & mut TraverseCtx < ' a > ) -> bool {
686
+ for ancestor in ctx. ancestors ( ) {
687
+ if ancestor. is_parent_of_statement ( ) {
688
+ return false ;
689
+ } else if matches ! ( ancestor, Ancestor :: PropertyDefinitionValue ( _) ) {
690
+ return true ;
691
+ }
692
+ }
693
+ unreachable ! ( )
694
+ }
695
+
643
696
/// Transforms a `MemberExpression` whose object is a `super` expression.
644
697
///
645
698
/// In the [`AsyncToGenerator`](crate::es2017::AsyncToGenerator) and
@@ -679,7 +732,11 @@ impl<'a> ArrowFunctionConverter<'a> {
679
732
assign_value : Option < & mut Expression < ' a > > ,
680
733
ctx : & mut TraverseCtx < ' a > ,
681
734
) -> Option < Expression < ' a > > {
682
- let super_methods = self . super_methods_stack . last_mut ( ) ?;
735
+ if !* self . super_needs_transform_stack . last ( ) {
736
+ return None ;
737
+ }
738
+
739
+ let super_methods = self . super_methods_stack . last_mut ( ) ;
683
740
684
741
let mut argument = None ;
685
742
let mut property = "" ;
@@ -757,7 +814,7 @@ impl<'a> ArrowFunctionConverter<'a> {
757
814
call : & mut CallExpression < ' a > ,
758
815
ctx : & mut TraverseCtx < ' a > ,
759
816
) -> Option < Expression < ' a > > {
760
- if self . super_methods_stack . last ( ) . is_none ( ) || !call. callee . is_member_expression ( ) {
817
+ if ! * self . super_needs_transform_stack . last ( ) || !call. callee . is_member_expression ( ) {
761
818
return None ;
762
819
}
763
820
@@ -796,7 +853,7 @@ impl<'a> ArrowFunctionConverter<'a> {
796
853
ctx : & mut TraverseCtx < ' a > ,
797
854
) -> Option < Expression < ' a > > {
798
855
// Check if the left of the assignment is a `super` member expression.
799
- if self . super_methods_stack . last ( ) . is_none ( )
856
+ if ! * self . super_needs_transform_stack . last ( )
800
857
|| !assignment. left . as_member_expression ( ) . is_some_and ( |m| m. object ( ) . is_super ( ) )
801
858
{
802
859
return None ;
@@ -1149,6 +1206,43 @@ impl<'a> ArrowFunctionConverter<'a> {
1149
1206
1150
1207
statements. insert ( 0 , stmt) ;
1151
1208
}
1209
+
1210
+ /// Wrap an arrow function with IIFE
1211
+ ///
1212
+ /// `() => {}` -> `(() => { return () => {}; })()`
1213
+ fn wrap_arrow_function_with_iife (
1214
+ span : Span ,
1215
+ scope_id : ScopeId ,
1216
+ expr : & mut Expression < ' a > ,
1217
+ ctx : & mut TraverseCtx < ' a > ,
1218
+ ) -> Expression < ' a > {
1219
+ let kind = FormalParameterKind :: ArrowFormalParameters ;
1220
+ let params = ctx. ast . formal_parameters ( SPAN , kind, ctx. ast . vec ( ) , NONE ) ;
1221
+ let statement = ctx. ast . statement_return ( SPAN , Some ( ctx. ast . move_expression ( expr) ) ) ;
1222
+ let statements = ctx. ast . vec1 ( statement) ;
1223
+ let body = ctx. ast . function_body ( SPAN , ctx. ast . vec ( ) , statements) ;
1224
+ let parent_scope_id = ctx
1225
+ . create_child_scope ( ctx. current_scope_id ( ) , ScopeFlags :: Arrow | ScopeFlags :: Function ) ;
1226
+ ctx. scopes_mut ( ) . change_parent_id ( scope_id, Some ( parent_scope_id) ) ;
1227
+ let arrow = ctx. ast . alloc_arrow_function_expression_with_scope_id (
1228
+ SPAN ,
1229
+ false ,
1230
+ false ,
1231
+ NONE ,
1232
+ params,
1233
+ NONE ,
1234
+ body,
1235
+ parent_scope_id,
1236
+ ) ;
1237
+ // IIFE
1238
+ ctx. ast . expression_call (
1239
+ span,
1240
+ Expression :: ArrowFunctionExpression ( arrow) ,
1241
+ NONE ,
1242
+ ctx. ast . vec ( ) ,
1243
+ false ,
1244
+ )
1245
+ }
1152
1246
}
1153
1247
1154
1248
/// Visitor for inserting `this` after `super` in constructor body.
0 commit comments