Skip to content

Commit 902c4c6

Browse files
committed
fix(transformer/arrow-functions): _this = this should be inserted after super call expression
1 parent f615bfa commit 902c4c6

File tree

13 files changed

+138
-57
lines changed

13 files changed

+138
-57
lines changed

crates/oxc_transformer/src/common/arrow_function_converter.rs

+60-9
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@
8989
9090
use compact_str::CompactString;
9191
use indexmap::IndexMap;
92-
use rustc_hash::{FxBuildHasher, FxHashSet};
92+
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
9393

94-
use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec};
94+
use oxc_allocator::{Address, Box as ArenaBox, GetAddress, Vec as ArenaVec};
9595
use oxc_ast::{ast::*, NONE};
9696
use oxc_data_structures::stack::{NonEmptyStack, SparseStack};
9797
use oxc_semantic::{ReferenceFlags, SymbolId};
@@ -102,7 +102,7 @@ use oxc_syntax::{
102102
};
103103
use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx};
104104

105-
use crate::EnvOptions;
105+
use crate::{context::TransformCtx, EnvOptions};
106106

107107
type FxIndexMap<K, V> = IndexMap<K, V, FxBuildHasher>;
108108

@@ -135,9 +135,12 @@ struct SuperMethodInfo<'a> {
135135
is_computed: bool,
136136
}
137137

138-
pub struct ArrowFunctionConverter<'a> {
138+
pub struct ArrowFunctionConverter<'a, 'ctx> {
139+
ctx: &'ctx TransformCtx<'a>,
139140
mode: ArrowFunctionConverterMode,
140141
this_var_stack: SparseStack<BoundIdentifier<'a>>,
142+
/// Stores the address of statement of containing `super()` expression
143+
super_call_addresses: FxHashMap<ScopeId, Address>,
141144
arguments_var_stack: SparseStack<BoundIdentifier<'a>>,
142145
arguments_needs_transform_stack: NonEmptyStack<bool>,
143146
renamed_arguments_symbol_ids: FxHashSet<SymbolId>,
@@ -146,8 +149,8 @@ pub struct ArrowFunctionConverter<'a> {
146149
super_methods: Option<FxIndexMap<SuperMethodKey<'a>, SuperMethodInfo<'a>>>,
147150
}
148151

149-
impl<'a> ArrowFunctionConverter<'a> {
150-
pub fn new(env: &EnvOptions) -> Self {
152+
impl<'a, 'ctx> ArrowFunctionConverter<'a, 'ctx> {
153+
pub fn new(env: &EnvOptions, ctx: &'ctx TransformCtx<'a>) -> Self {
151154
let mode = if env.es2015.arrow_function.is_some() {
152155
ArrowFunctionConverterMode::Enabled
153156
} else if env.es2017.async_to_generator || env.es2018.async_generator_functions {
@@ -157,8 +160,10 @@ impl<'a> ArrowFunctionConverter<'a> {
157160
};
158161
// `SparseStack`s are created with 1 empty entry, for `Program`
159162
Self {
163+
ctx,
160164
mode,
161165
this_var_stack: SparseStack::new(),
166+
super_call_addresses: FxHashMap::default(),
162167
arguments_var_stack: SparseStack::new(),
163168
arguments_needs_transform_stack: NonEmptyStack::new(false),
164169
renamed_arguments_symbol_ids: FxHashSet::default(),
@@ -167,7 +172,7 @@ impl<'a> ArrowFunctionConverter<'a> {
167172
}
168173
}
169174

170-
impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
175+
impl<'a, 'ctx> Traverse<'a> for ArrowFunctionConverter<'a, 'ctx> {
171176
// Note: No visitors for `TSModuleBlock` because `this` is not legal in TS module blocks.
172177
// <https://www.typescriptlang.org/play/?#code/HYQwtgpgzgDiDGEAEAxA9mpBvAsAKCSXjWCgBckANJAXiQAoBKWgPiTIAsBLKAbnwC++fGDQATAK4AbZACEQAJ2z5CxUhWp0mrdtz6D8QA>
173178

@@ -397,7 +402,7 @@ impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
397402
}
398403
}
399404

400-
impl<'a> ArrowFunctionConverter<'a> {
405+
impl<'a, 'ctx> ArrowFunctionConverter<'a, 'ctx> {
401406
/// Check if arrow function conversion is disabled
402407
fn is_disabled(&self) -> bool {
403408
self.mode == ArrowFunctionConverterMode::Disabled
@@ -436,6 +441,26 @@ impl<'a> ArrowFunctionConverter<'a> {
436441
.unwrap();
437442
ctx.generate_uid("this", target_scope_id, SymbolFlags::FunctionScopedVariable)
438443
});
444+
445+
if !self.super_call_addresses.is_empty() {
446+
let address = ctx
447+
.scopes()
448+
.get_parent_id(arrow_scope_id)
449+
.and_then(|scope_id| self.super_call_addresses.remove(&scope_id));
450+
if let Some(address) = address {
451+
// Insert a dummy address to indicate that should inserting `var _this;`
452+
// without `init` at the top of the statements.
453+
self.super_call_addresses.insert(arrow_scope_id, Address::DUMMY);
454+
let assignment = ctx.ast.expression_assignment(
455+
SPAN,
456+
AssignmentOperator::Assign,
457+
this_var.create_write_target(ctx),
458+
ctx.ast.expression_this(SPAN),
459+
);
460+
let statement = ctx.ast.statement_expression(SPAN, assignment);
461+
self.ctx.statement_injector.insert_after(&address, statement);
462+
}
463+
}
439464
Some(ctx.ast.alloc(this_var.create_spanned_read_reference(span, ctx)))
440465
}
441466

@@ -703,6 +728,25 @@ impl<'a> ArrowFunctionConverter<'a> {
703728
ctx: &mut TraverseCtx<'a>,
704729
) -> Option<Expression<'a>> {
705730
if self.super_methods.is_none() || !call.callee.is_member_expression() {
731+
// `super()`
732+
// Store the address in case we need to insert `var _this;` after it.
733+
if call.callee.is_super() {
734+
let scope_id = ctx.current_scope_id();
735+
self.super_call_addresses.entry(scope_id).or_insert_with(|| {
736+
ctx.ancestors()
737+
.find(|ancestor| {
738+
matches!(
739+
ancestor,
740+
// const A = super():
741+
Ancestor::VariableDeclarationDeclarations(_)
742+
// super();
743+
| Ancestor::ExpressionStatementExpression(_)
744+
)
745+
})
746+
.unwrap()
747+
.address()
748+
});
749+
}
706750
return None;
707751
}
708752

@@ -1070,12 +1114,19 @@ impl<'a> ArrowFunctionConverter<'a> {
10701114

10711115
// `_this = this;`
10721116
if let Some(this_var) = this_var {
1117+
let init = if self.super_call_addresses.is_empty() {
1118+
Some(ctx.ast.expression_this(SPAN))
1119+
} else {
1120+
// Clear the dummy address.
1121+
self.super_call_addresses.clear();
1122+
None
1123+
};
10731124
Self::adjust_binding_scope(target_scope_id, &this_var, ctx);
10741125
let variable_declarator = ctx.ast.variable_declarator(
10751126
SPAN,
10761127
VariableDeclarationKind::Var,
10771128
this_var.create_binding_pattern(ctx),
1078-
Some(ctx.ast.expression_this(SPAN)),
1129+
init,
10791130
false,
10801131
);
10811132
declarations.push(variable_declarator);

crates/oxc_transformer/src/common/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub struct Common<'a, 'ctx> {
2525
var_declarations: VarDeclarations<'a, 'ctx>,
2626
statement_injector: StatementInjector<'a, 'ctx>,
2727
top_level_statements: TopLevelStatements<'a, 'ctx>,
28-
arrow_function_converter: ArrowFunctionConverter<'a>,
28+
arrow_function_converter: ArrowFunctionConverter<'a, 'ctx>,
2929
}
3030

3131
impl<'a, 'ctx> Common<'a, 'ctx> {
@@ -35,7 +35,7 @@ impl<'a, 'ctx> Common<'a, 'ctx> {
3535
var_declarations: VarDeclarations::new(ctx),
3636
statement_injector: StatementInjector::new(ctx),
3737
top_level_statements: TopLevelStatements::new(ctx),
38-
arrow_function_converter: ArrowFunctionConverter::new(options),
38+
arrow_function_converter: ArrowFunctionConverter::new(options, ctx),
3939
}
4040
}
4141
}

tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/options.json

-10
This file was deleted.

tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/output.js

-12
This file was deleted.

tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/private-loose/foobar/reason.txt

-2
This file was deleted.

tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/public-loose/foobar/options.json

-10
This file was deleted.

tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/public-loose/foobar/output.js

-8
This file was deleted.

tasks/transform_conformance/overrides/babel-plugin-transform-class-properties/test/fixtures/public-loose/foobar/reason.txt

-2
This file was deleted.

tasks/transform_conformance/snapshots/oxc.snap.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
commit: 54a8389f
22

3-
Passed: 120/138
3+
Passed: 122/140
44

55
# All Passed:
66
* babel-plugin-transform-class-static-block
@@ -42,7 +42,7 @@ after transform: SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), R
4242
rebuilt : SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), ReferenceId(10)]
4343

4444

45-
# babel-plugin-transform-async-to-generator (14/15)
45+
# babel-plugin-transform-async-to-generator (16/17)
4646
* super/nested/input.js
4747
x Output mismatch
4848

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Outer {
2+
constructor() {
3+
async () => { return [this, 2]; };
4+
5+
class Inner extends Outer{
6+
constructor() {
7+
if (condition) {
8+
const _super = super()
9+
this.fn = async () => { return [this, 1]; };
10+
}
11+
12+
super()
13+
async () => { return [this, 2]; };
14+
}
15+
}
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class Outer {
2+
constructor() {
3+
var _this = this;
4+
babelHelpers.asyncToGenerator(function* () {
5+
return [_this, 2];
6+
});
7+
8+
class Inner extends Outer {
9+
constructor() {
10+
var _this2;
11+
12+
if (condition) {
13+
const _super = super();
14+
_this2 = this;
15+
this.fn = babelHelpers.asyncToGenerator(function* () {
16+
return [_this2, 1];
17+
});
18+
}
19+
20+
super();
21+
_this2 = this;
22+
babelHelpers.asyncToGenerator(function* () {
23+
return [_this2, 2];
24+
});
25+
}
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class C extends S {
2+
constructor() {
3+
if (condition) {
4+
const _super = super()
5+
this.fn = async () => { return [this, 1]; };
6+
}
7+
8+
super()
9+
async () => { return [this, 2]; };
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class C extends S {
2+
constructor() {
3+
var _this;
4+
if (condition) {
5+
const _super = super();
6+
_this = this;
7+
this.fn = babelHelpers.asyncToGenerator(function* () {
8+
return [_this, 1];
9+
});
10+
}
11+
12+
super();
13+
_this = this;
14+
babelHelpers.asyncToGenerator(function* () {
15+
return [_this, 2];
16+
});
17+
}
18+
}

0 commit comments

Comments
 (0)