fix(es/compat): Fix label handling of block_scoping (#4198)

This commit is contained in:
Austaras 2022-03-31 05:16:34 +08:00 committed by GitHub
parent 9a3591a424
commit c946236fcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 140 additions and 97 deletions

View File

@ -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;

View File

@ -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,
})))),
});

View File

@ -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(),