mirror of
https://github.com/swc-project/swc.git
synced 2024-11-24 10:12:42 +03:00
fix(es/compat): Fix syntax context of async-to-generator
(#6741)
**Description:** Previously, the `async-to-generator` produced invalid AST, in the aspect of span hygiene. [Playground](https://play.swc.rs/?version=1.3.24&code=H4sIAAAAAAAAAz1MbQqAIBT77yn2UyG6gNQJuoSZRCAa7xkk4d1TicZgH7C5%2B4yUYGPgBE7ZuyWazREmSIVpxiMAcumi0C3ANZxyvQ6%2Fqa8CehxjaGNpOAcLaWjn%2F6KhKN1dGaoULSpfHPTdxn8AAAA%3D&config=H4sIAAAAAAAAA0WOSwrDMAxE76K1F22hXfgE3fQQxlWCi39ICsQY3z12cMlOjObNTIUfW9AVsiFGGheXKGYHDVIysiWXBRQId2kxnrEpwF2QovFv9BmJQQtt2D2GVpROIj9u92enfEqMk1MQXHRLGR02hUzIfL1MXP3f2XpFSN9tCPWccWa%2BoF0Zk3P8mcYxoR3Kj7IYzwAAAA%3D%3D). It generate two bindings for `args` so it's invalid. **Related issue:** - Closes https://github.com/swc-project/swc/issues/6730.
This commit is contained in:
parent
1602f66fe6
commit
206c0dbebe
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4044,6 +4044,7 @@ dependencies = [
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"rustc-hash",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
|
26
crates/swc/tests/fixture/issues-6xxx/6730/input/.swcrc
Normal file
26
crates/swc/tests/fixture/issues-6xxx/6730/input/.swcrc
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": false
|
||||
},
|
||||
"target": "es2015",
|
||||
"loose": false,
|
||||
"minify": {
|
||||
"compress": false,
|
||||
"mangle": {
|
||||
"toplevel": false,
|
||||
"keep_classnames": false,
|
||||
"keep_fnames": false,
|
||||
"keep_private_props": false,
|
||||
"ie8": false,
|
||||
"safari10": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
},
|
||||
"minify": false,
|
||||
"isModule": true
|
||||
}
|
11
crates/swc/tests/fixture/issues-6xxx/6730/input/index.ts
Normal file
11
crates/swc/tests/fixture/issues-6xxx/6730/input/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Plugin, PluginBuild } from 'esbuild';
|
||||
|
||||
export const styleLoader = (): Plugin => {
|
||||
return {
|
||||
setup(build: PluginBuild) {
|
||||
build.onLoad(async (args) => {
|
||||
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
13
crates/swc/tests/fixture/issues-6xxx/6730/output/index.ts
Normal file
13
crates/swc/tests/fixture/issues-6xxx/6730/output/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import r from "@swc/helpers/src/_async_to_generator.mjs";
|
||||
export const styleLoader = ()=>{
|
||||
return {
|
||||
setup (t) {
|
||||
t.onLoad(function() {
|
||||
var t = r(function*(r) {});
|
||||
return function(r) {
|
||||
return t.apply(this, arguments);
|
||||
};
|
||||
}());
|
||||
}
|
||||
};
|
||||
};
|
@ -812,6 +812,10 @@ fn should_visit() {
|
||||
|
||||
#[testing::fixture("tests/fixture/**/input/")]
|
||||
#[testing::fixture("tests/vercel/**/input/")]
|
||||
fn fixture(input_dir: PathBuf) {
|
||||
tests(input_dir)
|
||||
}
|
||||
|
||||
fn tests(input_dir: PathBuf) {
|
||||
let output = input_dir.parent().unwrap().join("output");
|
||||
|
||||
|
@ -64,7 +64,7 @@ export var listOfUser = function(t) {
|
||||
2
|
||||
];
|
||||
});
|
||||
}), function(r, e) {
|
||||
}), function(r, n) {
|
||||
return e.apply(this, arguments);
|
||||
}));
|
||||
};
|
||||
|
@ -19,7 +19,7 @@ export const listOfUser = function(n) {
|
||||
postgreSQL.query(t, null, function(n, t) {
|
||||
n ? e(n) : r(t.rows);
|
||||
});
|
||||
}), function(r, e) {
|
||||
}), function(r, n) {
|
||||
return e.apply(this, arguments);
|
||||
}));
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ use swc_atoms::js_word;
|
||||
use swc_common::{pass::Either, util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::{
|
||||
contains_arguments, contains_this_expr, find_pat_ids, undefined, ExprFactory,
|
||||
contains_arguments, contains_this_expr, find_pat_ids, undefined, ExprFactory, Remapper,
|
||||
};
|
||||
use swc_ecma_visit::VisitMutWith;
|
||||
|
||||
@ -13,7 +13,7 @@ use super::{util::NormalMultiReplacer, Optimizer};
|
||||
#[cfg(feature = "debug")]
|
||||
use crate::debug::dump;
|
||||
use crate::{
|
||||
compress::optimize::{util::Remapper, Ctx},
|
||||
compress::optimize::Ctx,
|
||||
mode::Mode,
|
||||
util::{idents_captured_by, idents_used_by, make_number},
|
||||
};
|
||||
@ -578,7 +578,7 @@ where
|
||||
if self.vars.inline_with_multi_replacer(body) {
|
||||
self.changed = true;
|
||||
}
|
||||
body.visit_mut_with(&mut Remapper { vars: remap });
|
||||
body.visit_mut_with(&mut Remapper::new(&remap));
|
||||
exprs.push(body.take());
|
||||
|
||||
report_change!("inline: Inlining a call to an arrow function");
|
||||
@ -889,7 +889,7 @@ where
|
||||
if self.vars.inline_with_multi_replacer(body) {
|
||||
self.changed = true;
|
||||
}
|
||||
body.visit_mut_with(&mut Remapper { vars: remap });
|
||||
body.visit_mut_with(&mut Remapper::new(&remap));
|
||||
|
||||
let mut vars = Vec::new();
|
||||
let mut exprs = Vec::new();
|
||||
|
@ -5,7 +5,7 @@ use std::{
|
||||
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::{util::take::Take, Span, SyntaxContext, DUMMY_SP};
|
||||
use swc_common::{util::take::Take, Span, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::perf::{Parallel, ParallelExt};
|
||||
use swc_ecma_utils::{ExprCtx, ExprExt};
|
||||
@ -185,24 +185,6 @@ pub(crate) fn is_valid_for_lhs(e: &Expr) -> bool {
|
||||
!matches!(e, Expr::Lit(..) | Expr::Unary(..))
|
||||
}
|
||||
|
||||
/// Variable remapper
|
||||
///
|
||||
/// - Used for evaluating IIFEs
|
||||
|
||||
pub(crate) struct Remapper {
|
||||
pub vars: FxHashMap<Id, SyntaxContext>,
|
||||
}
|
||||
|
||||
impl VisitMut for Remapper {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_ident(&mut self, i: &mut Ident) {
|
||||
if let Some(new_ctxt) = self.vars.get(&i.to_id()).copied() {
|
||||
i.span.ctxt = new_ctxt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A visitor responsible for inlining special kind of variables and removing
|
||||
/// (some) unused variables. Due to the order of visit, the main visitor cannot
|
||||
/// handle all edge cases and this type is the complement for it.
|
||||
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"defaults": false
|
||||
}
|
45
crates/swc_ecma_minifier/tests/fixture/issues/6730/input.js
Normal file
45
crates/swc_ecma_minifier/tests/fixture/issues/6730/input.js
Normal file
@ -0,0 +1,45 @@
|
||||
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
||||
try {
|
||||
var info = gen[key](arg);
|
||||
var value = info.value;
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (info.done) {
|
||||
resolve(value);
|
||||
} else {
|
||||
Promise.resolve(value).then(_next, _throw);
|
||||
}
|
||||
}
|
||||
function _asyncToGenerator(fn) {
|
||||
return function () {
|
||||
var self = this, args = arguments;
|
||||
return new Promise(function (resolve, reject) {
|
||||
var gen = fn.apply(self, args);
|
||||
function _next(value) {
|
||||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
||||
}
|
||||
function _throw(err) {
|
||||
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
||||
}
|
||||
_next(undefined);
|
||||
});
|
||||
};
|
||||
}
|
||||
export const styleLoader = () => {
|
||||
return {
|
||||
name: 'style-loader',
|
||||
setup(build) {
|
||||
build.onLoad({
|
||||
filter: /.*/,
|
||||
namespace: 'less'
|
||||
}, function () {
|
||||
var _ref = _asyncToGenerator(function* (args) { });
|
||||
return function (args) {
|
||||
return _ref.apply(this, arguments);
|
||||
};
|
||||
}());
|
||||
}
|
||||
};
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"toplevel": true
|
||||
}
|
42
crates/swc_ecma_minifier/tests/fixture/issues/6730/output.js
Normal file
42
crates/swc_ecma_minifier/tests/fixture/issues/6730/output.js
Normal file
@ -0,0 +1,42 @@
|
||||
function n(n, e, t, r, o, u, i) {
|
||||
try {
|
||||
var a = n[u](i);
|
||||
var c = a.value;
|
||||
} catch (s) {
|
||||
t(s);
|
||||
return;
|
||||
}
|
||||
if (a.done) e(c);
|
||||
else Promise.resolve(c).then(r, o);
|
||||
}
|
||||
function e(e) {
|
||||
return function() {
|
||||
var t = this, r = arguments;
|
||||
return new Promise(function(o, u) {
|
||||
var i = e.apply(t, r);
|
||||
function a(e) {
|
||||
n(i, o, u, a, c, "next", e);
|
||||
}
|
||||
function c(e) {
|
||||
n(i, o, u, a, c, "throw", e);
|
||||
}
|
||||
a(void 0);
|
||||
});
|
||||
};
|
||||
}
|
||||
export const styleLoader = ()=>{
|
||||
return {
|
||||
name: 'style-loader',
|
||||
setup (n) {
|
||||
n.onLoad({
|
||||
filter: /.*/,
|
||||
namespace: 'less'
|
||||
}, function() {
|
||||
var n = e(function*(n) {});
|
||||
return function(e) {
|
||||
return n.apply(this, arguments);
|
||||
};
|
||||
}());
|
||||
}
|
||||
};
|
||||
};
|
@ -6,9 +6,9 @@ use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::{helper, helper_expr, perf::Check};
|
||||
use swc_ecma_transforms_macros::fast_path;
|
||||
use swc_ecma_utils::{
|
||||
contains_this_expr,
|
||||
contains_this_expr, find_pat_ids,
|
||||
function::{FnEnvHoister, FnWrapperResult, FunctionWrapper},
|
||||
private_ident, quote_ident, ExprFactory, StmtLike,
|
||||
private_ident, quote_ident, ExprFactory, Remapper, StmtLike,
|
||||
};
|
||||
use swc_ecma_visit::{
|
||||
as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith,
|
||||
@ -393,6 +393,16 @@ impl<C: Comments> Actual<C> {
|
||||
/// `_asyncToGenerator(function*() {})` from `async function() {}`;
|
||||
#[tracing::instrument(level = "info", skip_all)]
|
||||
fn make_fn_ref(mut expr: FnExpr) -> Expr {
|
||||
{
|
||||
let param_ids: Vec<Id> = find_pat_ids(&expr.function.params);
|
||||
let mapping = param_ids
|
||||
.into_iter()
|
||||
.map(|id| (id, SyntaxContext::empty().apply_mark(Mark::new())))
|
||||
.collect();
|
||||
|
||||
expr.function.visit_mut_with(&mut Remapper::new(&mapping));
|
||||
}
|
||||
|
||||
expr.function.body.visit_mut_with(&mut AsyncFnBodyHandler {
|
||||
is_async_generator: expr.function.is_generator,
|
||||
});
|
||||
|
@ -24,6 +24,7 @@ indexmap = "1.6.1"
|
||||
num_cpus = "1.13.1"
|
||||
once_cell = "1.10.0"
|
||||
rayon = {version = "1.5.1", optional = true}
|
||||
rustc-hash = "1.1.0"
|
||||
swc_atoms = {version = "0.4.32", path = "../swc_atoms"}
|
||||
swc_common = {version = "0.29.25", path = "../swc_common"}
|
||||
swc_ecma_ast = {version = "0.95.9", path = "../swc_ecma_ast"}
|
||||
|
@ -18,6 +18,7 @@ use std::{
|
||||
ops::Add,
|
||||
};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use swc_atoms::{js_word, JsWord};
|
||||
use swc_common::{collections::AHashSet, Mark, Span, Spanned, SyntaxContext, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
@ -2825,6 +2826,31 @@ pub fn contains_top_level_await<V: VisitWith<TopLevelAwait>>(t: &V) -> bool {
|
||||
finder.found
|
||||
}
|
||||
|
||||
/// Variable remapper
|
||||
///
|
||||
/// This visitor modifies [SyntaxContext] while preserving the symbol of
|
||||
/// [Ident]s.
|
||||
|
||||
pub struct Remapper<'a> {
|
||||
vars: &'a FxHashMap<Id, SyntaxContext>,
|
||||
}
|
||||
|
||||
impl<'a> Remapper<'a> {
|
||||
pub fn new(vars: &'a FxHashMap<Id, SyntaxContext>) -> Self {
|
||||
Self { vars }
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for Remapper<'_> {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_ident(&mut self, i: &mut Ident) {
|
||||
if let Some(new_ctxt) = self.vars.get(&i.to_id()).copied() {
|
||||
i.span.ctxt = new_ctxt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use swc_common::{input::StringInput, BytePos};
|
||||
|
Loading…
Reference in New Issue
Block a user