mirror of
https://github.com/swc-project/swc.git
synced 2024-11-27 04:47:03 +03:00
fix(es/compat): Fix label handling of block_scoping
(#4198)
This commit is contained in:
parent
9a3591a424
commit
c946236fcc
@ -79,10 +79,7 @@ export function selectRooms(building) {
|
||||
FLOOR_MAPPING[levelId],
|
||||
];
|
||||
};
|
||||
for(var _iterator2 = sortedRooms[Symbol.iterator](), _step2; !(_iteratorNormalCompletion1 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion1 = true){
|
||||
var _ret = _loop(_iterator2, _step2);
|
||||
if (_ret === "continue") continue;
|
||||
}
|
||||
for(var _iterator2 = sortedRooms[Symbol.iterator](), _step2; !(_iteratorNormalCompletion1 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion1 = true)_loop(_iterator2, _step2);
|
||||
} catch (err) {
|
||||
_didIteratorError1 = true;
|
||||
_iteratorError1 = err;
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::mem::take;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use smallvec::SmallVec;
|
||||
use swc_atoms::js_word;
|
||||
use swc_atoms::{js_word, JsWord};
|
||||
use swc_common::{
|
||||
chain, collections::AHashMap, util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP,
|
||||
};
|
||||
@ -176,9 +177,9 @@ impl BlockScoping {
|
||||
|
||||
let mut flow_helper = FlowHelper {
|
||||
all: &args,
|
||||
has_continue: false,
|
||||
has_break: false,
|
||||
has_return: false,
|
||||
label: IndexMap::new(),
|
||||
mutated,
|
||||
in_switch_case: false,
|
||||
in_nested_loop: false,
|
||||
@ -264,7 +265,7 @@ impl BlockScoping {
|
||||
type_args: None,
|
||||
};
|
||||
|
||||
if flow_helper.has_return || flow_helper.has_continue || flow_helper.has_break {
|
||||
if flow_helper.has_return || flow_helper.has_break || !flow_helper.label.is_empty() {
|
||||
let ret = private_ident!("_ret");
|
||||
|
||||
let mut stmts = vec![
|
||||
@ -282,11 +283,9 @@ impl BlockScoping {
|
||||
})),
|
||||
];
|
||||
|
||||
let use_switch = flow_helper.has_break && flow_helper.has_continue;
|
||||
|
||||
let check_ret = if flow_helper.has_return {
|
||||
if flow_helper.has_return {
|
||||
// if (_typeof(_ret) === "object") return _ret.v;
|
||||
Some(
|
||||
stmts.push(
|
||||
IfStmt {
|
||||
span: DUMMY_SP,
|
||||
test: Box::new(Expr::Bin(BinExpr {
|
||||
@ -315,88 +314,51 @@ impl BlockScoping {
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
if use_switch {
|
||||
let mut cases = vec![];
|
||||
|
||||
if flow_helper.has_break {
|
||||
cases.push(SwitchCase {
|
||||
if flow_helper.has_break {
|
||||
stmts.push(
|
||||
IfStmt {
|
||||
span: DUMMY_SP,
|
||||
test: Some(Box::new(quote_str!("break").into())),
|
||||
// TODO: Handle labelled statements
|
||||
cons: vec![Stmt::Break(BreakStmt {
|
||||
test: ret.clone().make_eq(quote_str!("break")).into(),
|
||||
cons: Stmt::Break(BreakStmt {
|
||||
span: DUMMY_SP,
|
||||
label: None,
|
||||
})],
|
||||
});
|
||||
}
|
||||
|
||||
if flow_helper.has_continue {
|
||||
cases.push(SwitchCase {
|
||||
span: DUMMY_SP,
|
||||
test: Some(Box::new(quote_str!("continue").into())),
|
||||
// TODO: Handle labelled statements
|
||||
cons: vec![Stmt::Continue(ContinueStmt {
|
||||
span: DUMMY_SP,
|
||||
label: None,
|
||||
})],
|
||||
});
|
||||
}
|
||||
|
||||
cases.extend(check_ret.map(|stmt| SwitchCase {
|
||||
span: DUMMY_SP,
|
||||
test: None,
|
||||
cons: vec![stmt],
|
||||
}));
|
||||
})
|
||||
.into(),
|
||||
alt: None,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
if !flow_helper.label.is_empty() {
|
||||
stmts.push(
|
||||
SwitchStmt {
|
||||
span: DUMMY_SP,
|
||||
discriminant: Box::new(ret.into()),
|
||||
cases,
|
||||
cases: flow_helper
|
||||
.label
|
||||
.into_iter()
|
||||
.map(|(key, label)| SwitchCase {
|
||||
span: DUMMY_SP,
|
||||
test: Some(Box::new(key.into())),
|
||||
cons: vec![match label {
|
||||
Label::Break(id) => Stmt::Break(BreakStmt {
|
||||
span: DUMMY_SP,
|
||||
label: Some(id),
|
||||
}),
|
||||
|
||||
Label::Continue(id) => Stmt::Continue(ContinueStmt {
|
||||
span: DUMMY_SP,
|
||||
label: Some(id),
|
||||
}),
|
||||
}],
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
} else {
|
||||
//
|
||||
if flow_helper.has_break {
|
||||
stmts.push(
|
||||
IfStmt {
|
||||
span: DUMMY_SP,
|
||||
test: ret.clone().make_eq(quote_str!("break")).into(),
|
||||
// TODO: Handle labelled statements
|
||||
cons: Stmt::Break(BreakStmt {
|
||||
span: DUMMY_SP,
|
||||
label: None,
|
||||
})
|
||||
.into(),
|
||||
alt: None,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
if flow_helper.has_continue {
|
||||
stmts.push(
|
||||
IfStmt {
|
||||
span: DUMMY_SP,
|
||||
test: ret.make_eq(quote_str!("continue")).into(),
|
||||
// TODO: Handle labelled statements
|
||||
cons: Stmt::Continue(ContinueStmt {
|
||||
span: DUMMY_SP,
|
||||
label: None,
|
||||
})
|
||||
.into(),
|
||||
alt: None,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
stmts.extend(check_ret);
|
||||
}
|
||||
|
||||
*body = Box::new(
|
||||
@ -715,11 +677,11 @@ impl Visit for InfectionFinder<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FlowHelper<'a> {
|
||||
has_continue: bool,
|
||||
has_break: bool,
|
||||
has_return: bool,
|
||||
// label cannot be shadowed, so it's pretty safe to use JsWord
|
||||
label: IndexMap<JsWord, Label>,
|
||||
all: &'a Vec<Id>,
|
||||
mutated: AHashMap<Id, SyntaxContext>,
|
||||
in_switch_case: bool,
|
||||
@ -727,6 +689,11 @@ struct FlowHelper<'a> {
|
||||
in_nested_loop: bool,
|
||||
}
|
||||
|
||||
enum Label {
|
||||
Break(Ident),
|
||||
Continue(Ident),
|
||||
}
|
||||
|
||||
impl<'a> FlowHelper<'a> {
|
||||
fn check(&mut self, i: Id) {
|
||||
if self.all.contains(&i) {
|
||||
@ -803,34 +770,50 @@ impl VisitMut for FlowHelper<'_> {
|
||||
let span = node.span();
|
||||
|
||||
match node {
|
||||
Stmt::Continue(..) => {
|
||||
Stmt::Continue(ContinueStmt { label, .. }) => {
|
||||
if self.in_nested_loop {
|
||||
return;
|
||||
}
|
||||
|
||||
self.has_continue = true;
|
||||
let value = if let Some(label) = label {
|
||||
let value: JsWord = format!("continue|{}", label.sym).into();
|
||||
self.label
|
||||
.insert(value.clone(), Label::Continue(label.clone()));
|
||||
value
|
||||
} else {
|
||||
"continue".into()
|
||||
};
|
||||
|
||||
*node = Stmt::Return(ReturnStmt {
|
||||
span,
|
||||
arg: Some(
|
||||
Expr::Lit(Lit::Str(Str {
|
||||
span,
|
||||
value: "continue".into(),
|
||||
value,
|
||||
raw: None,
|
||||
}))
|
||||
.into(),
|
||||
),
|
||||
});
|
||||
}
|
||||
Stmt::Break(..) => {
|
||||
Stmt::Break(BreakStmt { label, .. }) => {
|
||||
if self.in_switch_case || self.in_nested_loop {
|
||||
return;
|
||||
}
|
||||
self.has_break = true;
|
||||
let value = if let Some(label) = label {
|
||||
let value: JsWord = format!("break|{}", label.sym).into();
|
||||
self.label
|
||||
.insert(value.clone(), Label::Break(label.clone()));
|
||||
value
|
||||
} else {
|
||||
self.has_break = true;
|
||||
"break".into()
|
||||
};
|
||||
*node = Stmt::Return(ReturnStmt {
|
||||
span,
|
||||
arg: Some(Box::new(Expr::Lit(Lit::Str(Str {
|
||||
span,
|
||||
value: "break".into(),
|
||||
value,
|
||||
raw: None,
|
||||
})))),
|
||||
});
|
||||
|
@ -241,10 +241,7 @@ test!(
|
||||
};
|
||||
var vars = [];
|
||||
var elem = null, name, val;
|
||||
for(var i = 0; i < this.elements.length; i++){
|
||||
var _ret = _loop(i);
|
||||
if (_ret === \"continue\") continue;
|
||||
}
|
||||
for(var i = 0; i < this.elements.length; i++)_loop(i);
|
||||
return vars;
|
||||
};
|
||||
"
|
||||
@ -361,10 +358,7 @@ test!(
|
||||
if (i1 % 2 === 0) return i = i1, "continue";
|
||||
i = i1, void 0;
|
||||
};
|
||||
for(var i = 0; i < 5; i++){
|
||||
var _ret = _loop(i);
|
||||
if (_ret === "continue") continue;
|
||||
}
|
||||
for(var i = 0; i < 5; i++)_loop(i);
|
||||
"#
|
||||
);
|
||||
|
||||
@ -998,6 +992,75 @@ test!(
|
||||
"
|
||||
);
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|_| block_scoping(),
|
||||
issue_4196,
|
||||
"
|
||||
for (let i = 0; i < 2; i++) {
|
||||
if (i === 0) continue
|
||||
if (i === 1) break
|
||||
|
||||
[1].forEach((_) => {
|
||||
console.log(i)
|
||||
})
|
||||
}
|
||||
",
|
||||
r#"
|
||||
var _loop = function(i) {
|
||||
if (i === 0) return "continue";
|
||||
if (i === 1) return "break";
|
||||
[
|
||||
1
|
||||
].forEach((_)=>{
|
||||
console.log(i);
|
||||
});
|
||||
};
|
||||
for(var i = 0; i < 2; i++){
|
||||
var _ret = _loop(i);
|
||||
if (_ret === "break") break;
|
||||
}
|
||||
"#
|
||||
);
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|_| block_scoping(),
|
||||
labeled_break,
|
||||
"
|
||||
a:
|
||||
b:
|
||||
for (let i = 0; i < 2; i++) {
|
||||
if (i === 0) continue a
|
||||
if (i === 1) break b
|
||||
|
||||
[1].forEach((_) => {
|
||||
console.log(i)
|
||||
})
|
||||
}
|
||||
",
|
||||
r#"
|
||||
var _loop = function(i) {
|
||||
if (i === 0) return "continue|a";
|
||||
if (i === 1) return "break|b";
|
||||
[
|
||||
1
|
||||
].forEach((_)=>{
|
||||
console.log(i);
|
||||
});
|
||||
};
|
||||
a: b: for(var i = 0; i < 2; i++){
|
||||
var _ret = _loop(i);
|
||||
switch(_ret){
|
||||
case "continue|a":
|
||||
continue a;
|
||||
case "break|b":
|
||||
break b;
|
||||
}
|
||||
}
|
||||
"#
|
||||
);
|
||||
|
||||
test_exec!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|_| block_scoping(),
|
||||
|
Loading…
Reference in New Issue
Block a user