use crate::util::{prepend_stmts, ExprFactory};
use arrayvec::ArrayVec;
use swc_common::{Fold, FoldWith, Mark, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
pub fn parameters() -> Params {
Params
}
#[derive(Clone, Copy)]
pub struct Params;
noop_fold_type!(Params);
impl Params {
fn fold_fn_like(&mut self, ps: Vec<Param>, body: BlockStmt) -> (Vec<Param>, BlockStmt) {
let body = validate!(body);
let mut params = vec![];
let mut decls = vec![];
let mut unpack_rest = None;
let mut decls_after_unpack = vec![];
for (i, param) in ps.into_iter().enumerate() {
let span = param.span();
match param.pat {
Pat::Ident(..) => params.push(param),
Pat::Array(..) | Pat::Object(..) => {
let binding = private_ident!(span, "param");
params.push(Param {
pat: Pat::Ident(binding.clone()),
..param
});
decls.push(VarDeclarator {
span,
name: param.pat,
init: Some(box Expr::Ident(binding)),
definite: false,
})
}
Pat::Assign(..) => {
let binding = private_ident!(span, "param");
params.push(Param {
span: DUMMY_SP,
decorators: Default::default(),
pat: Pat::Ident(binding.clone()),
});
decls.push(VarDeclarator {
span,
name: param.pat,
init: Some(box Expr::Ident(binding)),
definite: false,
})
}
Pat::Rest(RestPat { arg, .. }) => {
assert!(unpack_rest.is_none());
let mark = Mark::fresh(Mark::root());
let idx_ident = quote_ident!(span.apply_mark(mark), "_key");
let len_ident = quote_ident!(span.apply_mark(mark), "_len");
let arg = match *arg {
Pat::Ident(ident) => ident,
arg => {
let tmp_ident = quote_ident!(span.apply_mark(mark), "_tmp");
decls_after_unpack.push(VarDeclarator {
span: DUMMY_SP,
name: arg,
init: Some(box tmp_ident.clone().into()),
definite: false,
});
tmp_ident
}
};
let make_minus_i = |ident: &Ident, min_zero: bool| -> Expr {
if i == 0 {
ident.clone().into()
} else {
let bin: Expr = validate!(BinExpr {
span,
left: box Expr::Ident(ident.clone()),
op: op!(bin, "-"),
right: box Expr::Lit(Lit::Num(Number {
span,
value: i as f64,
})),
})
.into();
if !min_zero {
return bin;
}
validate!(Expr::Cond(CondExpr {
span,
test: box BinExpr {
span,
left: box len_ident.clone().into(),
op: op!(">"),
right: box Expr::Lit(Lit::Num(Number {
span,
value: i as _,
})),
}
.into(),
cons: box bin,
alt: box Expr::Lit(Lit::Num(Number { span, value: 0.0 })),
}))
}
};
unpack_rest = Some(Stmt::For(ForStmt {
span,
init: Some(VarDeclOrExpr::VarDecl(VarDecl {
kind: VarDeclKind::Let,
span,
decls: vec![
VarDeclarator {
span,
name: Pat::Ident(len_ident.clone()),
init: Some(member_expr!(span, arguments.length)),
definite: false,
},
VarDeclarator {
span,
name: Pat::Ident(arg.clone()),
init: Some(box Expr::New(NewExpr {
span,
callee: box quote_ident!("Array").into(),
args: Some(vec![{
make_minus_i(&len_ident, true).as_arg()
}]),
type_args: Default::default(),
})),
definite: false,
},
VarDeclarator {
span,
name: Pat::Ident(idx_ident.clone()),
init: Some(box Expr::Lit(Lit::Num(Number {
span,
value: i as f64,
}))),
definite: false,
},
],
declare: false,
})),
test: Some(box Expr::Bin(BinExpr {
span,
left: box idx_ident.clone().into(),
op: op!("<"),
right: box len_ident.clone().into(),
})),
update: Some(box Expr::Update(UpdateExpr {
span,
op: op!("++"),
prefix: false,
arg: box idx_ident.clone().into(),
})),
body: box Stmt::Block(BlockStmt {
span,
stmts: vec![{
let prop = box Expr::Ident(idx_ident.clone());
let expr = AssignExpr {
span,
left: PatOrExpr::Expr(
box arg.computed_member(make_minus_i(&idx_ident, false)),
),
op: op!("="),
right: box validate!(MemberExpr {
span: DUMMY_SP,
obj: ExprOrSuper::Expr(
box quote_ident!(span, "arguments").into(),
),
computed: true,
prop,
})
.into(),
}
.into_stmt();
validate!(expr)
}],
}),
}))
}
_ => params.push(param),
}
}
let mut stmts = body.stmts;
let mut iter: ArrayVec<[_; 3]> = Default::default();
if !decls.is_empty() {
iter.push(Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
decls: validate!(decls),
declare: false,
})))
}
iter.extend(validate!(unpack_rest));
if !decls_after_unpack.is_empty() {
iter.push(Stmt::Decl(Decl::Var(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
decls: validate!(decls_after_unpack),
declare: false,
})));
}
prepend_stmts(&mut stmts, iter.into_iter());
(
params,
BlockStmt {
span: DUMMY_SP,
stmts,
},
)
}
}
impl_fold_fn!(Params);