mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 06:39:17 +00:00
fix(transformer): limit arguments capture to lowered async functions (#26317)
For targets that support async functions but not async generators, plain async methods and arrows remain native. Async-only converter mode still captured `arguments` for all of them, exposing generated `_arguments` bindings even though their bodies never move.
Gate `arguments` capture on the transform that owns the function body. Native async functions now remain untouched, while lowered async generator methods still capture their original arguments.
## Example
This native async method previously changed from:
```js
async method() {
void arguments;
return eval("typeof _arguments");
}
```
to:
```js
async method() {
var _arguments = arguments;
void _arguments;
return eval("typeof _arguments");
}
```
The generated binding changed the return value from `"undefined"` to `"object"`. The method now remains unchanged.
Verified with focused output/runtime conformance, full transformer output conformance, and `cargo test -p oxc_transformer`.
Stacked on #26227.
AI assistance was used to diagnose the transform boundary, implement the fix, and add tests. I remain responsible for reviewing and understanding the changes before merge.
This commit is contained in:
1 parent
789969f8a1
commit
f45a9ee58b
7 files changed
+91
-11
No files matched your search
@@ -282,7 +282,8 @@ impl<'a> Traverse<'a, TransformState<'a>> for ArrowFunctionConverter<'a> {
|
||||
) {
|
||||
if self.is_async_only() {
|
||||
let previous = *self.arguments_needs_transform_stack.last();
|
||||
self.arguments_needs_transform_stack.push(previous || arrow.r#async);
|
||||
self.arguments_needs_transform_stack
|
||||
.push(previous || self.will_transform_async_function(arrow.r#async, false));
|
||||
|
||||
if Self::in_class_property_definition_value(ctx) {
|
||||
self.this_var_stack.push(None);
|
||||
@@ -322,8 +323,9 @@ impl<'a> Traverse<'a, TransformState<'a>> for ArrowFunctionConverter<'a> {
|
||||
if self.is_async_only() {
|
||||
// Ignore arrow functions
|
||||
if let Ancestor::FunctionBody(func) = ctx.parent() {
|
||||
let is_async_method =
|
||||
*func.r#async() && Self::is_class_method_like_ancestor(ctx.ancestor(1));
|
||||
let is_async_method = self
|
||||
.will_transform_async_function(*func.r#async(), *func.generator())
|
||||
&& Self::is_class_method_like_ancestor(ctx.ancestor(1));
|
||||
self.arguments_needs_transform_stack.push(is_async_method);
|
||||
}
|
||||
}
|
||||
@@ -527,6 +529,16 @@ impl<'a> ArrowFunctionConverter<'a> {
|
||||
self.mode == ArrowFunctionConverterMode::AsyncOnly
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn will_transform_async_function(&self, r#async: bool, generator: bool) -> bool {
|
||||
r#async
|
||||
&& if generator {
|
||||
self.async_generator_functions_enabled
|
||||
} else {
|
||||
self.async_to_generator_enabled
|
||||
}
|
||||
}
|
||||
|
||||
fn get_this_identifier(
|
||||
&mut self,
|
||||
span: Span,
|
||||
@@ -641,12 +653,10 @@ impl<'a> ArrowFunctionConverter<'a> {
|
||||
// `this` will point to the wrong context.
|
||||
// To prevent this issue, we replace `this` with `_this`, treating it similarly
|
||||
// to how we handle arrow functions. Therefore, we return the `ScopeId` of the function.
|
||||
return if (self.async_to_generator_enabled
|
||||
|| (self.async_generator_functions_enabled && *func.generator()))
|
||||
&& *func.r#async()
|
||||
&& Self::is_class_method_like_ancestor(
|
||||
ancestors.next().unwrap()
|
||||
) {
|
||||
return if self
|
||||
.will_transform_async_function(*func.r#async(), *func.generator())
|
||||
&& Self::is_class_method_like_ancestor(ancestors.next().unwrap())
|
||||
{
|
||||
Some(func.scope_id().get().unwrap())
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
commit: 1eac4481
|
||||
|
||||
Passed: 272/400
|
||||
Passed: 273/401
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-class-static-block
|
||||
|
||||
@@ -2,7 +2,7 @@ commit: 1eac4481
|
||||
|
||||
node: v26.5.0
|
||||
|
||||
Passed: 16 of 18 (88.89%)
|
||||
Passed: 17 of 19 (89.47%)
|
||||
|
||||
Failures:
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
const object = {
|
||||
async method() {
|
||||
void arguments;
|
||||
return eval("typeof _arguments");
|
||||
},
|
||||
};
|
||||
|
||||
function outer() {
|
||||
return (async () => {
|
||||
void arguments;
|
||||
return eval("typeof _arguments2");
|
||||
})();
|
||||
}
|
||||
|
||||
return Promise.all([object.method(), outer()]).then(function (results) {
|
||||
expect(results[0]).toBe("undefined");
|
||||
expect(results[1]).toBe("undefined");
|
||||
});
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
const object = {
|
||||
async method() {
|
||||
return arguments;
|
||||
},
|
||||
async *generatorMethod() {
|
||||
return arguments;
|
||||
},
|
||||
};
|
||||
|
||||
class Example {
|
||||
async method() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
async *generatorMethod() {
|
||||
return arguments;
|
||||
}
|
||||
}
|
||||
|
||||
function outer() {
|
||||
return async () => arguments;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugins": [
|
||||
"transform-async-generator-functions"
|
||||
]
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
const object = {
|
||||
async method() {
|
||||
return arguments;
|
||||
},
|
||||
generatorMethod() {
|
||||
var _arguments = arguments;
|
||||
return babelHelpers.wrapAsyncGenerator(function* () {
|
||||
return _arguments;
|
||||
})();
|
||||
}
|
||||
};
|
||||
class Example {
|
||||
async method() {
|
||||
return arguments;
|
||||
}
|
||||
generatorMethod() {
|
||||
var _arguments2 = arguments;
|
||||
return babelHelpers.wrapAsyncGenerator(function* () {
|
||||
return _arguments2;
|
||||
})();
|
||||
}
|
||||
}
|
||||
function outer() {
|
||||
return async () => arguments;
|
||||
}
|
||||
Reference in new issue
Block a user