perf(transformer/arrow-functions): bail out of visiting early when inserting _this = this after super() (#8482)

In arrow functions transform (including async-to-generator), we need to insert a `_this = this` assignment in parent function, if the arrow function uses `this`. If the arrow function is in constructor of a class which `extend`s another class, this assignment needs to be inserted after `super()`, because `this` is invalid before a `super()` call has occurred.

Optimize the visitor which searches class constructor for `super()` for the common case where `super()` call occurs as a top-level statement early in the constructor. e.g.

```js
class C extends S {
    constructor() {
        super();
        // ... lots more code here ...
    }
}
```

In this case, we can insert `_this = this;` statement after `super();` and then exit the visitor. There's no need search the rest of the constructor for further `super()` calls, because `_this` is definitely now initialized prior to any of that code.
This commit is contained in:
overlookmotel 2025-01-14 13:09:57 +00:00
parent 3e05055783
commit 53ef263077

View file

@ -1310,24 +1310,24 @@ impl<'a> VisitMut<'a> for ConstructorBodyThisAfterSuperInserter<'a, '_> {
// `super()` can't appear in a nested function
}
/// `super()` -> `super(); _this = this;`
/// `super();` -> `super(); _this = this;`
fn visit_statements(&mut self, statements: &mut ArenaVec<'a, Statement<'a>>) {
let mut new_stmts = vec![];
for (index, stmt) in statements.iter_mut().enumerate() {
if matches!(stmt, Statement::ExpressionStatement(expr_stmt) if expr_stmt.expression.is_super_call_expression())
{
let assignment = self.create_assignment_to_this_temp_var();
let new_stmt = self.ctx.ast.statement_expression(SPAN, assignment);
new_stmts.push((index, new_stmt));
} else {
self.visit_statement(stmt);
let assignment = self.ctx.ast.statement_expression(SPAN, assignment);
statements.insert(index + 1, assignment);
// `super();` found as top-level statement in this block of statements.
// No need to continue visiting later statements, because `_this` is definitely assigned
// to at this point - no need to assign to it again.
// This means we don't visit the whole constructor in the common case where `super();`
// appears as a top-level statement early in class constructor
// `constructor() { super(); blah; blah; blah; }`.
break;
}
}
for (index, new_stmt) in new_stmts.into_iter().rev() {
// insert the new statement after the super call
statements.insert(index + 1, new_stmt);
self.visit_statement(stmt);
}
}