mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 18:43:44 +00:00
fix(transformer): hoist lowered async declarations (#22770)
Fixes semantic scope mismatches when async function declarations are lowered inside sloppy block scopes.
Before this change, `async_to_generator` kept the original async function binding, and the generated helper binding, in the current block scope. After transform those declarations are no longer async:
```js
with (obj) {
async function ref() {}
}
```
becomes roughly:
```js
with (obj) {
function ref() {
return _ref.apply(this, arguments);
}
function _ref() {
_ref = babelHelpers.asyncToGenerator(function* () {});
return _ref.apply(this, arguments);
}
}
```
A fresh semantic rebuild treats those lowered plain function declarations with Annex B hoisting rules, so in sloppy block scopes their bindings can belong to the enclosing var scope instead of the block scope. The transformed semantic tree was still using the pre-lowered block placement, which caused rebuild mismatches in Test262 coverage.
This PR makes `async_to_generator` choose the same binding scope that a rebuilt semantic tree would use for the emitted plain function declarations, while preserving the existing behavior for TypeScript, strict mode, var scopes, and cases where the hoist scope already has a binding.
This commit is contained in:
1 parent
3a7a606544
commit
018c0e57ec
2 files changed
+35
-138
No files matched your search
@@ -451,9 +451,17 @@ impl<'a> AsyncGeneratorExecutor<'a> {
|
||||
Self::create_placeholder_params(&wrapper_function.params, wrapper_scope_id, ctx);
|
||||
let params = mem::replace(&mut wrapper_function.params, params);
|
||||
|
||||
let function_binding_scope_id =
|
||||
Self::function_declaration_binding_scope_id(wrapper_function.id.as_ref(), ctx);
|
||||
if function_binding_scope_id != ctx.current_scope_id()
|
||||
&& let Some(id) = &wrapper_function.id
|
||||
{
|
||||
Self::move_binding_identifier_to_target_scope(function_binding_scope_id, id, ctx);
|
||||
}
|
||||
|
||||
let bound_ident = Self::create_bound_identifier(
|
||||
wrapper_function.id.as_ref(),
|
||||
ctx.current_scope_id(),
|
||||
Self::function_declaration_binding_scope_id(None, ctx),
|
||||
SymbolFlags::Function,
|
||||
ctx,
|
||||
);
|
||||
@@ -510,6 +518,31 @@ impl<'a> AsyncGeneratorExecutor<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the binding scope a rebuilt semantic tree would use for a plain function
|
||||
/// declaration emitted at the current position.
|
||||
fn function_declaration_binding_scope_id(
|
||||
id: Option<&BindingIdentifier<'a>>,
|
||||
ctx: &TraverseCtx<'a>,
|
||||
) -> ScopeId {
|
||||
if ctx.state.source_type.is_typescript() {
|
||||
return ctx.current_scope_id();
|
||||
}
|
||||
|
||||
let scope_flags = ctx.current_scope_flags();
|
||||
if scope_flags.is_var() || scope_flags.is_strict_mode() {
|
||||
return ctx.current_scope_id();
|
||||
}
|
||||
|
||||
let hoist_scope_id = ctx.current_hoist_scope_id();
|
||||
if let Some(id) = id
|
||||
&& ctx.scoping().scope_has_binding(hoist_scope_id, id.name)
|
||||
{
|
||||
ctx.current_scope_id()
|
||||
} else {
|
||||
hoist_scope_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms async arrow functions into generator functions wrapped in the asyncToGenerator helper.
|
||||
pub(self) fn transform_arrow_function(
|
||||
&self,
|
||||
|
||||
@@ -2,7 +2,7 @@ commit: de8e621c
|
||||
|
||||
semantic_test262 Summary:
|
||||
AST Parsed : 47240/47240 (100.00%)
|
||||
Positive Passed: 47020/47240 (99.53%)
|
||||
Positive Passed: 47028/47240 (99.55%)
|
||||
semantic Error: tasks/coverage/test262/test/language/eval-code/direct/async-func-decl-a-following-parameter-is-named-arguments-declare-arguments-and-assign.js
|
||||
Scope flags mismatch:
|
||||
after transform: ScopeId(4): ScopeFlags(Function)
|
||||
@@ -760,67 +760,11 @@ Scope flags mismatch:
|
||||
after transform: ScopeId(6): ScopeFlags(Function)
|
||||
rebuilt : ScopeId(2): ScopeFlags(Function | DirectEval)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/async-function/unscopables-with-in-nested-fn.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_asyncToGenerator", "callCount", "count", "v"]
|
||||
rebuilt : ScopeId(0): ["_asyncToGenerator", "_ref", "callCount", "count", "ref", "v"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_ref", "ref"]
|
||||
rebuilt : ScopeId(1): []
|
||||
Symbol scope ID mismatch for "ref":
|
||||
after transform: SymbolId(3): ScopeId(1)
|
||||
rebuilt : SymbolId(4): ScopeId(0)
|
||||
Symbol scope ID mismatch for "_ref":
|
||||
after transform: SymbolId(7): ScopeId(1)
|
||||
rebuilt : SymbolId(6): ScopeId(0)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/async-function/unscopables-with.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_asyncToGenerator", "callCount", "count", "v"]
|
||||
rebuilt : ScopeId(0): ["_asyncToGenerator", "_ref", "callCount", "count", "ref", "v"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_ref", "ref"]
|
||||
rebuilt : ScopeId(1): []
|
||||
Symbol scope ID mismatch for "ref":
|
||||
after transform: SymbolId(3): ScopeId(1)
|
||||
rebuilt : SymbolId(4): ScopeId(0)
|
||||
Symbol scope ID mismatch for "_ref":
|
||||
after transform: SymbolId(7): ScopeId(1)
|
||||
rebuilt : SymbolId(6): ScopeId(0)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/async-generator/eval-var-scope-syntax-err.js
|
||||
Scope flags mismatch:
|
||||
after transform: ScopeId(4): ScopeFlags(Function)
|
||||
rebuilt : ScopeId(2): ScopeFlags(Function | DirectEval)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/async-generator/unscopables-with-in-nested-fn.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_wrapAsyncGenerator", "callCount", "count", "v"]
|
||||
rebuilt : ScopeId(0): ["_ref", "_wrapAsyncGenerator", "callCount", "count", "ref", "v"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_ref", "ref"]
|
||||
rebuilt : ScopeId(1): []
|
||||
Symbol scope ID mismatch for "ref":
|
||||
after transform: SymbolId(3): ScopeId(1)
|
||||
rebuilt : SymbolId(4): ScopeId(0)
|
||||
Symbol scope ID mismatch for "_ref":
|
||||
after transform: SymbolId(7): ScopeId(1)
|
||||
rebuilt : SymbolId(6): ScopeId(0)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/async-generator/unscopables-with.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_wrapAsyncGenerator", "callCount", "count", "v"]
|
||||
rebuilt : ScopeId(0): ["_ref", "_wrapAsyncGenerator", "callCount", "count", "ref", "v"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_ref", "ref"]
|
||||
rebuilt : ScopeId(1): []
|
||||
Symbol scope ID mismatch for "ref":
|
||||
after transform: SymbolId(3): ScopeId(1)
|
||||
rebuilt : SymbolId(4): ScopeId(0)
|
||||
Symbol scope ID mismatch for "_ref":
|
||||
after transform: SymbolId(7): ScopeId(1)
|
||||
rebuilt : SymbolId(6): ScopeId(0)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/class/cpn-class-decl-fields-computed-property-name-from-arrow-function-expression.js
|
||||
Scope flags mismatch:
|
||||
after transform: ScopeId(2): ScopeFlags(StrictMode | Function | Arrow)
|
||||
@@ -1209,34 +1153,6 @@ Scope flags mismatch:
|
||||
after transform: ScopeId(1): ScopeFlags(StrictMode | DirectEval)
|
||||
rebuilt : ScopeId(1): ScopeFlags(StrictMode)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/for-await-of/async-func-decl-dstr-obj-id-put-unresolvable-no-strict.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_asyncIterator", "_asyncToGenerator"]
|
||||
rebuilt : ScopeId(0): ["_asyncIterator", "_asyncToGenerator", "_fn", "fn"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_fn", "fn", "iterCount", "promise"]
|
||||
rebuilt : ScopeId(1): ["iterCount", "promise"]
|
||||
Symbol scope ID mismatch for "fn":
|
||||
after transform: SymbolId(1): ScopeId(1)
|
||||
rebuilt : SymbolId(3): ScopeId(0)
|
||||
Symbol scope ID mismatch for "_fn":
|
||||
after transform: SymbolId(10): ScopeId(1)
|
||||
rebuilt : SymbolId(4): ScopeId(0)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/for-await-of/async-func-decl-dstr-obj-prop-put-unresolvable-no-strict.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_asyncIterator", "_asyncToGenerator"]
|
||||
rebuilt : ScopeId(0): ["_asyncIterator", "_asyncToGenerator", "_fn", "fn"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_fn", "fn", "iterCount", "promise"]
|
||||
rebuilt : ScopeId(1): ["iterCount", "promise"]
|
||||
Symbol scope ID mismatch for "fn":
|
||||
after transform: SymbolId(1): ScopeId(1)
|
||||
rebuilt : SymbolId(3): ScopeId(0)
|
||||
Symbol scope ID mismatch for "_fn":
|
||||
after transform: SymbolId(10): ScopeId(1)
|
||||
rebuilt : SymbolId(4): ScopeId(0)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/for-await-of/async-func-decl-dstr-obj-rest-getter.js
|
||||
Scope parent mismatch:
|
||||
after transform: ScopeId(3): Some(ScopeId(2))
|
||||
@@ -1257,34 +1173,6 @@ Scope parent mismatch:
|
||||
after transform: ScopeId(3): Some(ScopeId(2))
|
||||
rebuilt : ScopeId(6): Some(ScopeId(5))
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/for-await-of/async-gen-decl-dstr-obj-id-put-unresolvable-no-strict.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_asyncIterator", "_awaitAsyncGenerator", "_wrapAsyncGenerator"]
|
||||
rebuilt : ScopeId(0): ["_asyncIterator", "_awaitAsyncGenerator", "_fn", "_wrapAsyncGenerator", "fn"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_fn", "fn", "iterCount", "promise"]
|
||||
rebuilt : ScopeId(1): ["iterCount", "promise"]
|
||||
Symbol scope ID mismatch for "fn":
|
||||
after transform: SymbolId(1): ScopeId(1)
|
||||
rebuilt : SymbolId(4): ScopeId(0)
|
||||
Symbol scope ID mismatch for "_fn":
|
||||
after transform: SymbolId(11): ScopeId(1)
|
||||
rebuilt : SymbolId(5): ScopeId(0)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/for-await-of/async-gen-decl-dstr-obj-prop-put-unresolvable-no-strict.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_asyncIterator", "_awaitAsyncGenerator", "_wrapAsyncGenerator"]
|
||||
rebuilt : ScopeId(0): ["_asyncIterator", "_awaitAsyncGenerator", "_fn", "_wrapAsyncGenerator", "fn"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_fn", "fn", "iterCount", "promise"]
|
||||
rebuilt : ScopeId(1): ["iterCount", "promise"]
|
||||
Symbol scope ID mismatch for "fn":
|
||||
after transform: SymbolId(1): ScopeId(1)
|
||||
rebuilt : SymbolId(4): ScopeId(0)
|
||||
Symbol scope ID mismatch for "_fn":
|
||||
after transform: SymbolId(11): ScopeId(1)
|
||||
rebuilt : SymbolId(5): ScopeId(0)
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/for-await-of/async-gen-decl-dstr-obj-rest-getter.js
|
||||
Scope parent mismatch:
|
||||
after transform: ScopeId(3): Some(ScopeId(2))
|
||||
@@ -1328,21 +1216,9 @@ after transform: SymbolId(1) "x"
|
||||
rebuilt : SymbolId(1) "x"
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/switch/scope-lex-async-function.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_asyncToGenerator"]
|
||||
rebuilt : ScopeId(0): ["_asyncToGenerator", "_x", "x"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_x", "x"]
|
||||
rebuilt : ScopeId(1): []
|
||||
Symbol scope ID mismatch for "x":
|
||||
after transform: SymbolId(0): ScopeId(1)
|
||||
rebuilt : SymbolId(1): ScopeId(0)
|
||||
Symbol reference IDs mismatch for "x":
|
||||
after transform: SymbolId(0): []
|
||||
rebuilt : SymbolId(1): [ReferenceId(7)]
|
||||
Symbol scope ID mismatch for "_x":
|
||||
after transform: SymbolId(1): ScopeId(1)
|
||||
rebuilt : SymbolId(2): ScopeId(0)
|
||||
Reference symbol mismatch for "x":
|
||||
after transform: <None>
|
||||
rebuilt : SymbolId(1) "x"
|
||||
@@ -1351,21 +1227,9 @@ after transform: ["arguments", "require", "x"]
|
||||
rebuilt : ["arguments", "require"]
|
||||
|
||||
semantic Error: tasks/coverage/test262/test/language/statements/switch/scope-lex-async-generator.js
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["_wrapAsyncGenerator"]
|
||||
rebuilt : ScopeId(0): ["_wrapAsyncGenerator", "_x", "x"]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(1): ["_x", "x"]
|
||||
rebuilt : ScopeId(1): []
|
||||
Symbol scope ID mismatch for "x":
|
||||
after transform: SymbolId(0): ScopeId(1)
|
||||
rebuilt : SymbolId(1): ScopeId(0)
|
||||
Symbol reference IDs mismatch for "x":
|
||||
after transform: SymbolId(0): []
|
||||
rebuilt : SymbolId(1): [ReferenceId(7)]
|
||||
Symbol scope ID mismatch for "_x":
|
||||
after transform: SymbolId(1): ScopeId(1)
|
||||
rebuilt : SymbolId(2): ScopeId(0)
|
||||
Reference symbol mismatch for "x":
|
||||
after transform: <None>
|
||||
rebuilt : SymbolId(1) "x"
|
||||
|
||||
Reference in new issue
Block a user