Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1,164 @@
use std::iter;
use swc_common::{util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_fold_type, noop_visit_mut_type, Fold, FoldWith, VisitMut, VisitMutWith};
use crate::{prepend_stmts, ExprFactory};
pub fn inject_after_super(c: &mut Constructor, mut exprs: Vec<Box<Expr>>) {
// Allow using super multiple time
let mut folder = Injector {
exprs: &mut exprs,
injected: false,
};
c.body = std::mem::take(&mut c.body).fold_with(&mut folder);
if !folder.injected {
if c.body.is_none() {
c.body = Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![],
});
}
// there was no super() call
prepend_stmts(
&mut c.body.as_mut().unwrap().stmts,
exprs.into_iter().map(|v| v.into_stmt()),
);
}
}
struct Injector<'a> {
injected: bool,
exprs: &'a mut Vec<Box<Expr>>,
}
impl<'a> Fold for Injector<'a> {
noop_fold_type!();
fn fold_class(&mut self, c: Class) -> Class {
c
}
fn fold_constructor(&mut self, n: Constructor) -> Constructor {
n
}
fn fold_function(&mut self, n: Function) -> Function {
n
}
fn fold_stmts(&mut self, stmts: Vec<Stmt>) -> Vec<Stmt> {
if self.exprs.is_empty() {
return stmts;
}
let mut buf = Vec::with_capacity(stmts.len() + 8);
stmts.into_iter().for_each(|stmt| {
if let Stmt::Expr(ExprStmt { ref expr, .. }) = stmt {
if let Expr::Call(CallExpr {
callee: Callee::Super(..),
..
}) = &**expr
{
self.injected = true;
buf.push(stmt);
buf.extend(self.exprs.clone().into_iter().map(|v| v.into_stmt()));
return;
}
}
let mut folder = Injector {
injected: false,
exprs: self.exprs,
};
let mut stmt = stmt.fold_children_with(&mut folder);
self.injected |= folder.injected;
if folder.injected {
buf.push(stmt);
} else {
let mut folder = ExprInjector {
injected: false,
exprs: self.exprs,
injected_tmp: None,
};
stmt.visit_mut_with(&mut folder);
self.injected |= folder.injected;
buf.extend(folder.injected_tmp.map(|ident| {
Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(ident.into()),
init: None,
definite: false,
}],
declare: false,
})))
}));
buf.push(stmt);
}
});
buf
}
}
/// Handles code like `foo(super())`
struct ExprInjector<'a> {
injected: bool,
exprs: &'a mut Vec<Box<Expr>>,
injected_tmp: Option<Ident>,
}
impl VisitMut for ExprInjector<'_> {
noop_visit_mut_type!();
fn visit_mut_class(&mut self, c: &mut Class) {
c.super_class.visit_mut_with(self);
}
fn visit_mut_constructor(&mut self, _: &mut Constructor) {}
fn visit_mut_expr(&mut self, expr: &mut Expr) {
expr.visit_mut_children_with(self);
if let Expr::Call(CallExpr {
callee: Callee::Super(..),
..
}) = expr
{
self.injected_tmp = Some(
self.injected_tmp
.take()
.unwrap_or_else(|| private_ident!("_temp")),
);
self.injected = true;
let e = expr.take();
*expr = Expr::Seq(SeqExpr {
span: DUMMY_SP,
exprs: iter::once(Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(Box::new(Pat::Ident(
self.injected_tmp.as_ref().cloned().unwrap().into(),
))),
op: op!("="),
right: Box::new(e),
})))
.chain(self.exprs.clone())
.chain(iter::once(Box::new(Expr::Ident(
self.injected_tmp.as_ref().cloned().unwrap(),
))))
.collect(),
})
}
}
fn visit_mut_function(&mut self, _: &mut Function) {}
}

View file

@ -0,0 +1,374 @@
use std::iter;
use swc_atoms::js_word;
use swc_common::{util::take::Take, Span, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
/// Extension methods for [Expr].
///
/// Note that many types implements `Into<Expr>` and you can do like
///
/// ```rust
/// use swc_ecma_utils::ExprFactory;
///
/// let _args = vec![0f64.as_arg()];
/// ```
///
/// to create literals. Almost all rust core types implements `Into<Expr>`.
#[allow(clippy::wrong_self_convention)]
pub trait ExprFactory: Into<Expr> {
/// Creates an [ExprOrSpread] using the given [Expr].
///
/// This is recommended way to create [ExprOrSpread].
///
/// # Example
///
/// ```rust
/// use swc_common::DUMMY_SP;
/// use swc_ecma_ast::*;
/// use swc_ecma_utils::ExprFactory;
///
/// let first = Lit::Num(Number {
/// span: DUMMY_SP,
/// value: 0.0,
/// raw: None,
/// });
/// let _args = vec![first.as_arg()];
/// ```
#[cfg_attr(not(debug_assertions), inline(always))]
fn as_arg(self) -> ExprOrSpread {
ExprOrSpread {
expr: Box::new(self.into()),
spread: None,
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn as_pat_or_expr(self) -> PatOrExpr {
PatOrExpr::Expr(Box::new(self.into()))
}
/// Creates an expression statement with `self`.
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_stmt(self) -> Stmt {
Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr: Box::new(self.into()),
})
}
/// Creates a statement whcih return `self`.
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_return_stmt(self) -> ReturnStmt {
ReturnStmt {
span: DUMMY_SP,
arg: Some(Box::new(self.into())),
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn as_callee(self) -> Callee {
Callee::Expr(Box::new(self.into()))
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn as_iife(self) -> CallExpr {
CallExpr {
span: DUMMY_SP,
callee: self.as_callee(),
args: Default::default(),
type_args: Default::default(),
}
}
/// create a ArrowExpr which return self
/// - `(params) => $self`
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_lazy_arrow(self, params: Vec<Pat>) -> ArrowExpr {
ArrowExpr {
span: DUMMY_SP,
params,
body: Box::new(BlockStmtOrExpr::from(self)),
is_async: false,
is_generator: false,
type_params: None,
return_type: None,
}
}
/// create a Function which return self
/// - `function(params) { return $self; }`
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_lazy_fn(self, params: Vec<Param>) -> Function {
Function {
params,
decorators: Default::default(),
span: DUMMY_SP,
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![self.into_return_stmt().into()],
}),
is_generator: false,
is_async: false,
type_params: None,
return_type: None,
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_lazy_auto(self, params: Vec<Pat>, support_arrow: bool) -> Expr {
if support_arrow {
self.into_lazy_arrow(params).into()
} else {
self.into_lazy_fn(params.into_iter().map(From::from).collect())
.into_fn_expr(None)
.into()
}
}
/// create a var declartor using self as init
/// - `var name = expr`
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_var_decl(self, kind: VarDeclKind, name: Pat) -> VarDecl {
let var_declarator = VarDeclarator {
span: DUMMY_SP,
name,
init: Some(Box::new(self.into())),
definite: false,
};
VarDecl {
span: DUMMY_SP,
kind,
declare: false,
decls: vec![var_declarator],
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_new_expr(self, span: Span, args: Option<Vec<ExprOrSpread>>) -> NewExpr {
NewExpr {
span,
callee: Box::new(self.into()),
args,
type_args: None,
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn apply(self, span: Span, this: Box<Expr>, args: Vec<ExprOrSpread>) -> Expr {
let apply = self.make_member(Ident::new(js_word!("apply"), span));
Expr::Call(CallExpr {
span,
callee: apply.as_callee(),
args: iter::once(this.as_arg()).chain(args).collect(),
type_args: None,
})
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn call_fn(self, span: Span, args: Vec<ExprOrSpread>) -> Expr {
Expr::Call(CallExpr {
span,
args,
callee: self
.make_member(Ident::new(js_word!("call"), span))
.as_callee(),
type_args: None,
})
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn as_call(self, span: Span, args: Vec<ExprOrSpread>) -> Expr {
Expr::Call(CallExpr {
span,
args,
callee: self.as_callee(),
type_args: None,
})
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn as_fn_decl(self) -> Option<FnDecl> {
match self.into() {
Expr::Fn(FnExpr {
ident: Some(ident),
function,
}) => Some(FnDecl {
ident,
declare: false,
function,
}),
_ => None,
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn as_class_decl(self) -> Option<ClassDecl> {
match self.into() {
Expr::Class(ClassExpr {
ident: Some(ident),
class,
}) => Some(ClassDecl {
ident,
declare: false,
class,
}),
_ => None,
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn wrap_with_paren(self) -> Expr {
let expr = Box::new(self.into());
let span = expr.span();
Expr::Paren(ParenExpr { expr, span })
}
/// Creates a binary expr `$self === `
#[cfg_attr(not(debug_assertions), inline(always))]
fn make_eq<T>(self, right: T) -> Expr
where
T: Into<Expr>,
{
self.make_bin(op!("==="), right)
}
/// Creates a binary expr `$self $op $rhs`
#[cfg_attr(not(debug_assertions), inline(always))]
fn make_bin<T>(self, op: BinaryOp, right: T) -> Expr
where
T: Into<Expr>,
{
let right = Box::new(right.into());
Expr::Bin(BinExpr {
span: DUMMY_SP,
left: Box::new(self.into()),
op,
right,
})
}
/// Creates a assign expr `$lhs $op $self`
#[cfg_attr(not(debug_assertions), inline(always))]
fn make_assign_to(self, op: AssignOp, left: PatOrExpr) -> Expr {
let right = Box::new(self.into());
Expr::Assign(AssignExpr {
span: DUMMY_SP,
left,
op,
right,
})
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn make_member<T>(self, prop: T) -> Expr
where
T: Into<Ident>,
{
Expr::Member(MemberExpr {
obj: Box::new(self.into()),
span: DUMMY_SP,
prop: MemberProp::Ident(prop.into()),
})
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn computed_member<T>(self, prop: T) -> Expr
where
T: Into<Expr>,
{
Expr::Member(MemberExpr {
obj: Box::new(self.into()),
span: DUMMY_SP,
prop: MemberProp::Computed(ComputedPropName {
span: DUMMY_SP,
expr: Box::new(prop.into()),
}),
})
}
}
impl<T: Into<Expr>> ExprFactory for T {}
pub trait IntoIndirectCall
where
Self: std::marker::Sized,
{
type Item;
fn into_indirect(self) -> Self::Item;
}
impl IntoIndirectCall for CallExpr {
type Item = CallExpr;
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_indirect(self) -> CallExpr {
let callee = self.callee.into_indirect();
CallExpr { callee, ..self }
}
}
impl IntoIndirectCall for Callee {
type Item = Callee;
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_indirect(self) -> Callee {
SeqExpr {
span: DUMMY_SP,
exprs: vec![0f64.into(), self.expect_expr()],
}
.as_callee()
}
}
impl IntoIndirectCall for TaggedTpl {
type Item = TaggedTpl;
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_indirect(mut self) -> Self {
Self {
tag: Box::new(
SeqExpr {
span: DUMMY_SP,
exprs: vec![0f64.into(), self.tag.take()],
}
.into(),
),
..self
}
}
}
pub trait FunctionFactory: Into<Function> {
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_fn_expr(self, ident: Option<Ident>) -> FnExpr {
FnExpr {
ident,
function: Box::new(self.into()),
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_fn_decl(self, ident: Ident) -> FnDecl {
FnDecl {
ident,
declare: false,
function: Box::new(self.into()),
}
}
#[cfg_attr(not(debug_assertions), inline(always))]
fn into_method_prop(self, key: PropName) -> MethodProp {
MethodProp {
key,
function: Box::new(self.into()),
}
}
}
impl<T: Into<Function>> FunctionFactory for T {}

View file

@ -0,0 +1,852 @@
use std::mem;
use indexmap::IndexMap;
use swc_atoms::{js_word, JsWord};
use swc_common::{util::take::Take, Span, Spanned, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use crate::ExprFactory;
#[derive(Default)]
struct SuperField {
computed: Option<Ident>,
ident: IndexMap<JsWord, Ident>,
}
/// Don't use it against function, it will stop if come across any function
/// use it against function body
#[derive(Default)]
pub struct FnEnvHoister {
unresolved_ctxt: SyntaxContext,
this: Option<Ident>,
args: Option<Ident>,
new_target: Option<Ident>,
super_get: SuperField,
super_set: SuperField,
super_update: SuperField,
arguments_disabled: bool,
this_disabled: bool,
super_disabled: bool,
in_pat: bool,
// extra ident for super["xx"] += 123
extra_ident: Vec<Ident>,
}
impl FnEnvHoister {
pub fn new(unresolved_ctxt: SyntaxContext) -> Self {
Self {
unresolved_ctxt,
..Default::default()
}
}
/// Disable hoisting of `arguments`
pub fn disable_arguments(&mut self) {
self.arguments_disabled = true;
}
/// Disable hoisting of `this`
pub fn disable_this(&mut self) {
self.this_disabled = true;
}
/// Disable hoisting of nodes realted to `super`
pub fn disable_super(&mut self) {
self.super_disabled = true;
}
pub fn take(&mut self) -> Self {
let mut new = Self {
unresolved_ctxt: self.unresolved_ctxt,
..Default::default()
};
mem::swap(self, &mut new);
new
}
pub fn to_decl(self) -> Vec<VarDeclarator> {
let Self {
this,
args,
new_target,
super_get,
super_set,
super_update,
..
} = self;
let mut decls = Vec::with_capacity(3);
if let Some(this_id) = this {
decls.push(VarDeclarator {
span: DUMMY_SP,
name: this_id.into(),
init: Some(Box::new(Expr::This(ThisExpr { span: DUMMY_SP }))),
definite: false,
});
}
if let Some(id) = args {
decls.push(VarDeclarator {
span: DUMMY_SP,
name: id.into(),
init: Some(Box::new(Expr::Ident(Ident::new(
js_word!("arguments"),
DUMMY_SP,
)))),
definite: false,
});
}
if let Some(id) = new_target {
decls.push(VarDeclarator {
span: DUMMY_SP,
name: id.into(),
init: Some(Box::new(Expr::MetaProp(MetaPropExpr {
span: DUMMY_SP,
kind: MetaPropKind::NewTarget,
}))),
definite: false,
});
}
extend_super(&mut decls, super_get, super_set, super_update);
decls
}
pub fn to_stmt(self) -> Option<Stmt> {
let decls = self.to_decl();
if decls.is_empty() {
None
} else {
Some(Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls,
}))))
}
}
pub fn to_stmt_in_subclass(self) -> (Option<Stmt>, Option<Ident>) {
let Self {
this,
args,
new_target,
super_get,
super_set,
super_update,
..
} = self;
let mut decls = Vec::with_capacity(3);
if let Some(this_id) = &this {
decls.push(VarDeclarator {
span: DUMMY_SP,
name: this_id.clone().into(),
init: None,
definite: false,
});
}
if let Some(id) = args {
decls.push(VarDeclarator {
span: DUMMY_SP,
name: id.into(),
init: Some(Box::new(Expr::Ident(Ident::new(
js_word!("arguments"),
DUMMY_SP,
)))),
definite: false,
});
}
if let Some(id) = new_target {
decls.push(VarDeclarator {
span: DUMMY_SP,
name: id.into(),
init: Some(Box::new(Expr::MetaProp(MetaPropExpr {
span: DUMMY_SP,
kind: MetaPropKind::NewTarget,
}))),
definite: false,
});
}
extend_super(&mut decls, super_get, super_set, super_update);
if decls.is_empty() {
(None, None)
} else {
(
Some(Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls,
})))),
this,
)
}
}
fn get_this(&mut self) -> Ident {
self.this
.get_or_insert_with(|| private_ident!("_this"))
.clone()
}
fn super_get(&mut self, prop_name: &JsWord, prop_span: Span) -> Ident {
if let Some(callee) = self.super_get.ident.get(prop_name) {
callee.clone()
} else {
let ident = private_ident!(prop_span, format!("_superprop_get_{}", prop_name));
self.super_get
.ident
.insert(prop_name.clone(), ident.clone());
ident
}
}
fn super_get_computed(&mut self, span: Span) -> Ident {
self.super_get
.computed
.get_or_insert_with(|| private_ident!(span, "_superprop_get"))
.clone()
}
fn super_set(&mut self, prop_name: &JsWord, prop_span: Span) -> Ident {
if let Some(callee) = self.super_set.ident.get(prop_name) {
callee.clone()
} else {
let ident = private_ident!(prop_span, format!("_superprop_set_{}", prop_name));
self.super_set
.ident
.insert(prop_name.clone(), ident.clone());
ident
}
}
fn super_set_computed(&mut self, span: Span) -> Ident {
self.super_set
.computed
.get_or_insert_with(|| private_ident!(span, "_superprop_set"))
.clone()
}
fn super_update(&mut self, prop_name: &JsWord, prop_span: Span) -> Ident {
if let Some(callee) = self.super_update.ident.get(prop_name) {
callee.clone()
} else {
self.super_get
.ident
.entry(prop_name.clone())
.or_insert_with(|| {
private_ident!(prop_span, format!("_superprop_get_{}", prop_name))
});
self.super_set
.ident
.entry(prop_name.clone())
.or_insert_with(|| {
private_ident!(prop_span, format!("_superprop_set_{}", prop_name))
});
let ident = private_ident!(prop_span, format!("_superprop_update_{}", prop_name));
self.super_update
.ident
.insert(prop_name.clone(), ident.clone());
ident
}
}
fn super_update_computed(&mut self, span: Span) -> Ident {
self.super_get
.computed
.get_or_insert_with(|| private_ident!(span, "_superprop_get"));
self.super_set
.computed
.get_or_insert_with(|| private_ident!(span, "_superprop_set"));
self.super_update
.computed
.get_or_insert_with(|| private_ident!(span, "_superprop_update"))
.clone()
}
}
impl VisitMut for FnEnvHoister {
noop_visit_mut_type!();
fn visit_mut_pat(&mut self, n: &mut Pat) {
let in_pat = self.in_pat;
self.in_pat = true;
n.visit_mut_children_with(self);
self.in_pat = in_pat;
}
fn visit_mut_expr(&mut self, e: &mut Expr) {
match e {
Expr::Ident(Ident { span, sym, .. })
if !self.arguments_disabled
&& *sym == js_word!("arguments")
&& (span.ctxt == self.unresolved_ctxt
|| span.ctxt == SyntaxContext::empty()) =>
{
let arguments = self
.args
.get_or_insert_with(|| private_ident!("_arguments"));
*e = Expr::Ident(arguments.clone());
}
Expr::This(..) if !self.this_disabled => {
let this = self.get_this();
*e = Expr::Ident(this);
}
Expr::MetaProp(MetaPropExpr {
kind: MetaPropKind::NewTarget,
..
}) => {
let target = self
.new_target
.get_or_insert_with(|| private_ident!("_newtarget"));
*e = Expr::Ident(target.clone());
}
// super.foo = 123 => super_get_foo = (value) => super.foo = value
Expr::Assign(AssignExpr {
left,
right,
span,
op,
}) => {
let expr = match left {
PatOrExpr::Expr(e) => e,
PatOrExpr::Pat(p) => {
if let Pat::Expr(e) = &mut **p {
e
} else {
e.visit_mut_children_with(self);
return;
}
}
};
if !self.super_disabled {
if let Expr::SuperProp(super_prop) = &mut **expr {
let left_span = super_prop.span;
match &mut super_prop.prop {
SuperProp::Computed(c) => {
let callee = self.super_set_computed(left_span);
let op = op.to_update();
let args = if let Some(op) = op {
let tmp = private_ident!("tmp");
self.extra_ident.push(tmp.clone());
vec![
Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(tmp.clone().into()),
op: op!("="),
right: c.expr.take(),
})
.as_arg(),
Expr::Bin(BinExpr {
span: DUMMY_SP,
left: Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: self
.super_get_computed(DUMMY_SP)
.as_callee(),
args: vec![tmp.as_arg()],
type_args: None,
})),
op,
right: right.take(),
})
.as_arg(),
]
} else {
vec![c.expr.take().as_arg(), right.take().as_arg()]
};
*e = Expr::Call(CallExpr {
span: *span,
args,
callee: callee.as_callee(),
type_args: None,
});
}
SuperProp::Ident(id) => {
let callee = self.super_set(&id.sym, left_span);
*e = Expr::Call(CallExpr {
span: *span,
args: vec![(if let Some(op) = op.to_update() {
Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: Box::new(
self.super_get(&id.sym, id.span)
.as_call(id.span, Vec::new()),
),
op,
right: right.take(),
}))
} else {
right.take()
})
.as_arg()],
callee: callee.as_callee(),
type_args: None,
});
}
}
}
}
e.visit_mut_children_with(self)
}
// super.foo() => super_get_foo = () => super.foo
Expr::Call(CallExpr {
span,
callee: Callee::Expr(expr),
args,
..
}) => {
if !self.super_disabled {
if let Expr::SuperProp(super_prop) = &mut **expr {
match &mut super_prop.prop {
SuperProp::Computed(c) => {
let callee = self.super_get_computed(super_prop.span);
let call = Expr::Call(CallExpr {
span: *span,
args: vec![c.expr.take().as_arg()],
callee: callee.as_callee(),
type_args: None,
});
let mut new_args = args.take();
new_args.insert(0, self.get_this().as_arg());
*e = call.call_fn(*span, new_args);
}
SuperProp::Ident(id) => {
let callee = self.super_get(&id.sym, super_prop.span);
let call = Expr::Call(CallExpr {
span: *span,
args: Vec::new(),
callee: callee.as_callee(),
type_args: None,
});
let mut new_args = args.take();
new_args.insert(0, self.get_this().as_arg());
*e = call.call_fn(*span, new_args);
}
}
};
}
e.visit_mut_children_with(self)
}
// super.foo ++
Expr::Update(UpdateExpr { arg, .. }) if arg.is_super_prop() => {
let in_pat = self.in_pat;
// NOTE: It's not in pat, but we need the `update` trick
self.in_pat = true;
arg.visit_mut_with(self);
self.in_pat = in_pat;
}
Expr::SuperProp(SuperPropExpr { prop, span, .. }) if !self.super_disabled => match prop
{
SuperProp::Computed(c) => {
c.expr.visit_mut_children_with(self);
*e = if self.in_pat {
Expr::Call(CallExpr {
span: *span,
args: vec![c.expr.take().as_arg()],
callee: self.super_update_computed(*span).as_callee(),
type_args: None,
})
.make_member(quote_ident!("_"))
} else {
Expr::Call(CallExpr {
span: *span,
args: vec![c.expr.take().as_arg()],
callee: self.super_get_computed(*span).as_callee(),
type_args: None,
})
};
}
SuperProp::Ident(id) => {
*e = if self.in_pat {
self.super_update(&id.sym, *span)
.make_member(quote_ident!("_"))
} else {
Expr::Call(CallExpr {
span: *span,
args: Vec::new(),
callee: self.super_get(&id.sym, *span).as_callee(),
type_args: None,
})
};
}
},
_ => e.visit_mut_children_with(self),
}
}
fn visit_mut_block_stmt(&mut self, b: &mut BlockStmt) {
b.visit_mut_children_with(self);
// we will not vist into fn/class so it's fine
if !self.extra_ident.is_empty() {
b.stmts.insert(
0,
Stmt::Decl(Decl::Var(Box::new(VarDecl {
kind: VarDeclKind::Var,
span: DUMMY_SP,
decls: self
.extra_ident
.take()
.into_iter()
.map(|ident| VarDeclarator {
span: DUMMY_SP,
name: ident.into(),
init: None,
definite: false,
})
.collect(),
declare: false,
}))),
)
}
}
fn visit_mut_block_stmt_or_expr(&mut self, b: &mut BlockStmtOrExpr) {
b.visit_mut_children_with(self);
// we will not vist into fn/class so it's fine
if !self.extra_ident.is_empty() {
if let BlockStmtOrExpr::Expr(e) = b {
*b = BlockStmtOrExpr::BlockStmt(BlockStmt {
span: DUMMY_SP,
stmts: vec![
Stmt::Decl(Decl::Var(Box::new(VarDecl {
kind: VarDeclKind::Var,
span: DUMMY_SP,
decls: self
.extra_ident
.take()
.into_iter()
.map(|ident| VarDeclarator {
span: DUMMY_SP,
name: ident.into(),
init: None,
definite: false,
})
.collect(),
declare: false,
}))),
Stmt::Return(ReturnStmt {
span: e.span(),
arg: Some(e.take()),
}),
],
})
}
}
}
/// Don't recurse into constructor
fn visit_mut_class(&mut self, _: &mut Class) {}
/// Don't recurse into fn
fn visit_mut_function(&mut self, _: &mut Function) {}
/// Don't recurse into getter/setter/method except computed key
fn visit_mut_getter_prop(&mut self, p: &mut GetterProp) {
if p.key.is_computed() {
p.key.visit_mut_with(self);
}
}
fn visit_mut_setter_prop(&mut self, p: &mut SetterProp) {
if p.key.is_computed() {
p.key.visit_mut_with(self);
}
}
fn visit_mut_method_prop(&mut self, p: &mut MethodProp) {
if p.key.is_computed() {
p.key.visit_mut_with(self);
}
}
}
pub fn init_this(stmts: &mut Vec<Stmt>, this_id: &Ident) {
stmts.visit_mut_children_with(&mut InitThis { this_id })
}
struct InitThis<'a> {
this_id: &'a Ident,
}
// babel is skip function and class property
impl<'a> VisitMut for InitThis<'a> {
noop_visit_mut_type!();
fn visit_mut_class(&mut self, _: &mut Class) {}
// babel will transform super() to super(); _this = this
// hopefully it will be meaningless
// fn visit_mut_stmts(&mut self, stmt: &mut Vec<Stmt>) {}
fn visit_mut_expr(&mut self, expr: &mut Expr) {
expr.visit_mut_children_with(self);
if let Expr::Call(
call_expr @ CallExpr {
callee: Callee::Super(..),
..
},
) = expr
{
let span = call_expr.span;
*expr = Expr::Paren(ParenExpr {
span,
expr: Box::new(Expr::Seq(SeqExpr {
span,
exprs: vec![
Box::new(Expr::Call(call_expr.take())),
Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(self.this_id.clone().into()),
op: AssignOp::Assign,
right: Box::new(Expr::This(ThisExpr { span: DUMMY_SP })),
})),
],
})),
})
}
}
}
fn extend_super(
decls: &mut Vec<VarDeclarator>,
get: SuperField,
set: SuperField,
update: SuperField,
) {
decls.extend(update.ident.into_iter().map(|(key, ident)| {
let value = private_ident!("v");
VarDeclarator {
span: DUMMY_SP,
name: ident.into(),
init: Some(Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props: vec![
Prop::Getter(GetterProp {
span: DUMMY_SP,
key: PropName::Ident(quote_ident!("_")),
type_ann: None,
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![Expr::Ident(
get.ident
.get(&key)
.cloned()
.expect("getter not found")
.without_loc(),
)
.as_call(DUMMY_SP, Default::default())
.into_return_stmt()
.into()],
}),
}),
Prop::Setter(SetterProp {
span: DUMMY_SP,
key: PropName::Ident(quote_ident!("_")),
param: value.clone().into(),
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![Expr::Ident(
set.ident
.get(&key)
.cloned()
.expect("setter not found")
.without_loc(),
)
.as_call(DUMMY_SP, vec![value.as_arg()])
.into_stmt()],
}),
}),
]
.into_iter()
.map(Box::new)
.map(From::from)
.collect(),
}))),
definite: false,
}
}));
if let Some(id) = update.computed {
let prop = private_ident!("_prop");
let value = private_ident!("v");
decls.push(VarDeclarator {
span: DUMMY_SP,
name: id.into(),
init: Some(Box::new(Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![prop.clone().into()],
body: Box::new(BlockStmtOrExpr::Expr(Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props: vec![
Prop::Getter(GetterProp {
span: DUMMY_SP,
key: PropName::Ident(quote_ident!("_")),
type_ann: None,
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![Expr::Ident(
get.computed
.clone()
.expect("getter computed not found")
.without_loc(),
)
.as_call(DUMMY_SP, vec![prop.clone().as_arg()])
.into_return_stmt()
.into()],
}),
}),
Prop::Setter(SetterProp {
span: DUMMY_SP,
key: PropName::Ident(quote_ident!("_")),
param: value.clone().into(),
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![Expr::Ident(
set.computed
.clone()
.expect("setter computed not found")
.without_loc(),
)
.as_call(DUMMY_SP, vec![prop.as_arg(), value.as_arg()])
.into_return_stmt()
.into()],
}),
}),
]
.into_iter()
.map(Box::new)
.map(From::from)
.collect(),
})))),
is_async: false,
is_generator: false,
return_type: None,
type_params: None,
}))),
definite: false,
});
}
decls.extend(get.ident.into_iter().map(|(key, ident)| VarDeclarator {
span: DUMMY_SP,
name: ident.without_loc().into(),
init: Some(Box::new(Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: Vec::new(),
body: Box::new(BlockStmtOrExpr::Expr(Box::new(Expr::SuperProp(
SuperPropExpr {
obj: Super { span: DUMMY_SP },
prop: SuperProp::Ident(quote_ident!(key)),
span: DUMMY_SP,
},
)))),
is_async: false,
is_generator: false,
return_type: None,
type_params: None,
}))),
definite: false,
}));
if let Some(id) = get.computed {
let param = private_ident!("_prop");
decls.push(VarDeclarator {
span: DUMMY_SP,
name: id.without_loc().into(),
init: Some(Box::new(Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![param.clone().into()],
body: Box::new(BlockStmtOrExpr::Expr(Box::new(Expr::SuperProp(
SuperPropExpr {
obj: Super { span: DUMMY_SP },
prop: SuperProp::Computed(ComputedPropName {
span: DUMMY_SP,
expr: Box::new(Expr::Ident(param)),
}),
span: DUMMY_SP,
},
)))),
is_async: false,
is_generator: false,
return_type: None,
type_params: None,
}))),
definite: false,
});
}
decls.extend(set.ident.into_iter().map(|(key, ident)| {
let value = private_ident!("_value");
VarDeclarator {
span: DUMMY_SP,
name: ident.without_loc().into(),
init: Some(Box::new(Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![value.clone().into()],
body: Box::new(BlockStmtOrExpr::Expr(Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Expr(Box::new(Expr::SuperProp(SuperPropExpr {
obj: Super { span: DUMMY_SP },
prop: SuperProp::Ident(quote_ident!(key)),
span: DUMMY_SP,
}))),
op: op!("="),
right: Box::new(Expr::Ident(value)),
})))),
is_async: false,
is_generator: false,
return_type: None,
type_params: None,
}))),
definite: false,
}
}));
if let Some(id) = set.computed {
let prop = private_ident!("_prop");
let value = private_ident!("_value");
decls.push(VarDeclarator {
span: DUMMY_SP,
name: id.without_loc().into(),
init: Some(Box::new(Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![prop.clone().into(), value.clone().into()],
body: Box::new(BlockStmtOrExpr::Expr(Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Expr(Box::new(Expr::SuperProp(SuperPropExpr {
obj: Super { span: DUMMY_SP },
prop: SuperProp::Computed(ComputedPropName {
span: DUMMY_SP,
expr: Box::new(Expr::Ident(prop)),
}),
span: DUMMY_SP,
}))),
op: op!("="),
right: Box::new(Expr::Ident(value)),
})))),
is_async: false,
is_generator: false,
return_type: None,
type_params: None,
}))),
definite: false,
});
}
}

View file

@ -0,0 +1,434 @@
use std::marker::PhantomData;
use swc_common::{util::take::Take, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use crate::ExprFactory;
pub struct FunctionWrapper<T> {
pub binding_ident: Option<Ident>,
pub function: Expr,
pub ignore_function_name: bool,
pub ignore_function_length: bool,
function_ident: Option<Ident>,
params: Vec<Param>,
_type: PhantomData<T>,
}
impl<T> FunctionWrapper<T> {
/// `get_params` clone only the parameters that count in function length.
fn get_params<'a, ParamsIter, Item>(params_iter: ParamsIter) -> Vec<Param>
where
Item: Into<&'a Param>,
ParamsIter: IntoIterator<Item = Item>,
{
params_iter
.into_iter()
.map(Into::into)
.map_while(|param| match param.pat {
Pat::Ident(..) => Some(param.clone()),
Pat::Array(..) | Pat::Object(..) => Some(Param {
span: param.span,
decorators: param.decorators.clone(),
pat: Pat::Ident(private_ident!("_").into()),
}),
_ => None,
})
.collect()
}
///
/// ```javascript
/// (function () {
/// var REF = FUNCTION;
/// return function NAME(PARAMS) {
/// return REF.apply(this, arguments);
/// };
/// })()
/// ```
fn build_anonymous_expression_wrapper(&mut self) -> Expr {
let name_ident = self.binding_ident.take();
let ref_ident = private_ident!("_ref");
let ref_decl: Decl = Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(ref_ident.clone().into()),
init: Some(Box::new(self.function.take())),
definite: false,
}],
declare: false,
})
.into();
let return_fn_stmt = {
let fn_expr = self.build_function_forward(ref_ident, name_ident);
ReturnStmt {
span: DUMMY_SP,
arg: Some(Box::new(fn_expr.into())),
}
}
.into();
let block_stmt = BlockStmt {
span: DUMMY_SP,
stmts: vec![ref_decl.into(), return_fn_stmt],
};
let function = Box::new(Function {
span: DUMMY_SP,
body: Some(block_stmt),
params: Default::default(),
is_generator: false,
is_async: false,
decorators: Default::default(),
return_type: Default::default(),
type_params: Default::default(),
});
FnExpr {
ident: None,
function,
}
.as_iife()
.into()
}
///
/// ```javascript
/// (function () {
/// var REF = FUNCTION;
/// function NAME(PARAMS) {
/// return REF.apply(this, arguments);
/// }
/// return NAME;
/// })()
/// ```
fn build_named_expression_wrapper(&mut self, name_ident: Ident) -> Expr {
let ref_ident = self.function_ident.as_ref().map_or_else(
|| private_ident!("_ref"),
|ident| private_ident!(ident.span, format!("_{}", ident.sym)),
);
let ref_stmt: Stmt = Stmt::Decl(
Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(ref_ident.clone().into()),
init: Some(Box::new(self.function.take())),
definite: false,
}],
declare: false,
})
.into(),
);
let fn_decl_stmt = {
let FnExpr { function, .. } = self.build_function_forward(ref_ident, None);
Stmt::Decl(
FnDecl {
ident: name_ident.clone(),
declare: false,
function,
}
.into(),
)
};
let return_stmt = Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(Box::new(name_ident.into())),
});
let block_stmt = BlockStmt {
span: DUMMY_SP,
stmts: vec![ref_stmt, fn_decl_stmt, return_stmt],
};
let function = Box::new(Function {
span: DUMMY_SP,
body: Some(block_stmt),
params: Default::default(),
is_generator: false,
is_async: false,
decorators: Default::default(),
return_type: Default::default(),
type_params: Default::default(),
});
FnExpr {
ident: None,
function,
}
.as_iife()
.into()
}
///
/// ```javascript
/// function NAME(PARAMS) {
/// return REF.apply(this, arguments);
/// }
/// function REF() {
/// REF = FUNCTION;
/// return REF.apply(this, arguments);
/// }
/// ```
fn build_declaration_wrapper(&mut self, name_ident: Option<Ident>) -> (FnExpr, FnDecl) {
let ref_ident = self.function_ident.as_ref().map_or_else(
|| private_ident!("_ref"),
|ident| private_ident!(ident.span, format!("_{}", ident.sym)),
);
// function NAME
let fn_expr = self.build_function_forward(ref_ident.clone(), name_ident);
let assign_stmt = AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Expr(Box::new(Expr::Ident(ref_ident.clone()))),
right: Box::new(self.function.take()),
}
.into_stmt();
// clone `return REF.apply(this, arguments);`
let return_ref_apply_stmt = fn_expr
.function
.body
.as_ref()
.expect("The `fn_expr` we construct cannot be None")
.stmts[0]
.clone();
let ref_fn_block_stmt = BlockStmt {
span: DUMMY_SP,
stmts: vec![assign_stmt, return_ref_apply_stmt],
};
// function REF
let ref_decl = FnDecl {
declare: false,
ident: ref_ident,
function: Box::new(Function {
span: DUMMY_SP,
is_async: false,
is_generator: false,
params: self.params.take(),
body: Some(ref_fn_block_stmt),
decorators: Default::default(),
type_params: Default::default(),
return_type: Default::default(),
}),
};
(fn_expr, ref_decl)
}
///
/// ```javascript
/// function NAME(PARAMS) {
/// return REF.apply(this, arguments);
/// }
/// ```
fn build_function_forward(&mut self, ref_ident: Ident, name_ident: Option<Ident>) -> FnExpr {
let apply = Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(Box::new(ref_ident.apply(
DUMMY_SP,
Box::new(Expr::This(ThisExpr { span: DUMMY_SP })),
vec![quote_ident!("arguments").as_arg()],
))),
});
FnExpr {
ident: name_ident,
function: Box::new(Function {
params: self.params.take(),
decorators: Default::default(),
span: DUMMY_SP,
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![apply],
}),
is_generator: false,
is_async: false,
type_params: Default::default(),
return_type: Default::default(),
}),
}
}
}
impl From<FnExpr> for FunctionWrapper<Expr> {
fn from(mut fn_expr: FnExpr) -> Self {
let function_ident = fn_expr.ident.take();
let params = Self::get_params(fn_expr.function.params.iter());
Self {
binding_ident: None,
function_ident,
params,
ignore_function_name: false,
ignore_function_length: false,
function: fn_expr.into(),
_type: Default::default(),
}
}
}
impl From<ArrowExpr> for FunctionWrapper<Expr> {
fn from(
ArrowExpr {
span,
params,
body,
is_async,
is_generator,
..
}: ArrowExpr,
) -> Self {
let body = Some(match *body {
BlockStmtOrExpr::BlockStmt(block) => block,
BlockStmtOrExpr::Expr(expr) => BlockStmt {
span: DUMMY_SP,
stmts: vec![Stmt::Return(ReturnStmt {
span: expr.span(),
arg: Some(expr),
})],
},
});
let function = Box::new(Function {
span,
params: params.into_iter().map(Into::into).collect(),
decorators: Default::default(),
body,
type_params: None,
return_type: None,
is_generator,
is_async,
});
let fn_expr = FnExpr {
ident: None,
function,
};
Self {
binding_ident: None,
function_ident: None,
ignore_function_name: false,
ignore_function_length: false,
params: Self::get_params(fn_expr.function.params.iter()),
function: fn_expr.into(),
_type: Default::default(),
}
}
}
#[allow(clippy::from_over_into)]
impl Into<Expr> for FunctionWrapper<Expr> {
/// If a function has a function name, it may be called recursively.
/// We use the named expression to hoist the function name internally
/// Therefore, its recursive calls refer to the correct identity.
///
/// Else
/// if a function has a binding name, it may be called recursively as well.
/// But it refer the binding name which exist the outer scope.
/// It is safe to using anonymous expression wrapper.
///
/// Optimization:
/// A function without a name cannot be recursively referenced by Ident.
/// It's safe to return the expr without wrapper if the params.len is 0.
fn into(mut self) -> Expr {
if let Some(name_ident) = self.function_ident.as_ref().cloned() {
self.build_named_expression_wrapper(name_ident)
} else if (!self.ignore_function_name && self.binding_ident.is_some())
|| (!self.ignore_function_length && !self.params.is_empty())
{
self.build_anonymous_expression_wrapper()
} else {
self.function
}
}
}
impl From<FnDecl> for FunctionWrapper<FnDecl> {
fn from(mut fn_decl: FnDecl) -> Self {
let function_ident = Some(fn_decl.ident.take());
let params = Self::get_params(fn_decl.function.params.iter());
Self {
binding_ident: None,
function_ident,
params,
ignore_function_name: false,
ignore_function_length: false,
function: FnExpr {
ident: None,
function: fn_decl.function,
}
.into(),
_type: Default::default(),
}
}
}
///
/// The result of declaration wrapper includes two parts.
/// `name_fn` is used to replace the original function.
/// `ref_fn` is an extra function called internally by `name_fn`.
///
/// ```javascript
/// function NAME(PARAMS) {
/// return REF.apply(this, arguments);
/// }
/// function REF() {
/// REF = FUNCTION;
/// return REF.apply(this, arguments);
/// }
/// ```
pub struct FnWrapperResult<N, R> {
pub name_fn: N,
pub ref_fn: R,
}
impl From<FunctionWrapper<FnDecl>> for FnWrapperResult<FnDecl, FnDecl> {
fn from(mut value: FunctionWrapper<FnDecl>) -> Self {
let name_ident = value
.function_ident
.clone()
.expect("`FunctionWrapper` converted from `FnDecl` definitely has `Ident`");
let (FnExpr { function, .. }, ref_fn) = value.build_declaration_wrapper(None);
FnWrapperResult {
name_fn: FnDecl {
ident: name_ident,
declare: false,
function,
},
ref_fn,
}
}
}
impl From<FunctionWrapper<Expr>> for FnWrapperResult<FnExpr, FnDecl> {
fn from(mut value: FunctionWrapper<Expr>) -> Self {
let name_ident = value
.function_ident
.clone()
.or_else(|| value.binding_ident.clone());
let (name_fn, ref_fn) = value.build_declaration_wrapper(name_ident);
FnWrapperResult { name_fn, ref_fn }
}
}

View file

@ -0,0 +1,5 @@
mod fn_env_hoister;
mod function_wrapper;
pub use fn_env_hoister::*;
pub use function_wrapper::*;

View file

@ -0,0 +1,80 @@
use swc_atoms::JsWord;
use swc_common::{Span, SyntaxContext};
use swc_ecma_ast::{BindingIdent, Id, Ident};
pub trait IdentLike: Sized {
fn from_ident(i: &Ident) -> Self;
fn to_id(&self) -> Id;
fn into_id(self) -> Id;
}
impl IdentLike for BindingIdent {
fn from_ident(i: &Ident) -> Self {
i.clone().into()
}
fn to_id(&self) -> Id {
(self.id.sym.clone(), self.id.span.ctxt())
}
fn into_id(self) -> Id {
self.id.into_id()
}
}
impl IdentLike for (JsWord, Span) {
#[inline]
fn from_ident(i: &Ident) -> Self {
(i.sym.clone(), i.span)
}
#[inline]
fn to_id(&self) -> Id {
(self.0.clone(), self.1.ctxt())
}
#[inline]
fn into_id(self) -> Id {
(self.0, self.1.ctxt())
}
}
impl IdentLike for (JsWord, SyntaxContext) {
#[inline]
fn from_ident(i: &Ident) -> Self {
(i.sym.clone(), i.span.ctxt())
}
#[inline]
fn to_id(&self) -> Id {
(self.0.clone(), self.1)
}
#[inline]
fn into_id(self) -> Id {
self
}
}
impl IdentLike for Ident {
#[inline]
fn from_ident(i: &Ident) -> Self {
Ident::new(i.sym.clone(), i.span)
}
#[inline]
fn to_id(&self) -> Id {
(self.sym.clone(), self.span.ctxt())
}
#[inline]
fn into_id(self) -> Id {
(self.sym, self.span.ctxt())
}
}
#[deprecated = "Use i.to_id() instead"]
#[inline(always)]
pub fn id(i: &Ident) -> Id {
(i.sym.clone(), i.span.ctxt())
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,118 @@
/// Shortcut for `quote_ident!(span.apply_mark(Mark::fresh(Mark::root())), s)`
#[macro_export]
macro_rules! private_ident {
($s:expr) => {
private_ident!($crate::swc_common::DUMMY_SP, $s)
};
($span:expr, $s:expr) => {{
let mark = $crate::swc_common::Mark::fresh($crate::swc_common::Mark::root());
let span = $span.apply_mark(mark);
$crate::swc_ecma_ast::Ident::new($s.into(), span)
}};
}
#[macro_export]
macro_rules! quote_ident {
($s:expr) => {
quote_ident!($crate::swc_common::DUMMY_SP, $s)
};
($span:expr, $s:expr) => {{
$crate::swc_ecma_ast::Ident::new($s.into(), $span)
}};
}
#[macro_export]
macro_rules! quote_str {
($s:expr) => {
quote_str!($crate::swc_common::DUMMY_SP, $s)
};
($span:expr, $s:expr) => {{
$crate::swc_ecma_ast::Str {
span: $span,
raw: None,
value: $s.into(),
}
}};
}
#[macro_export]
macro_rules! quote_expr {
($span:expr, null) => {{
use $crate::swc_ecma_ast::*;
Expr::Lit(Lit::Null(Null { span: $span }))
}};
($span:expr, undefined) => {{
box Expr::Ident(Ident::new(js_word!("undefined"), $span))
}};
}
/// Creates a member expression.
///
/// # Usage
/// ```rust,ignore
/// member_expr!(span, Function.bind.apply);
/// ```
///
/// Returns Box<[Expr](swc_ecma_ast::Expr)>.
#[macro_export]
macro_rules! member_expr {
($span:expr, $first:ident) => {{
use $crate::swc_ecma_ast::Expr;
Box::new(Expr::Ident($crate::quote_ident!($span, stringify!($first))))
}};
($span:expr, $first:ident . $($rest:tt)+) => {{
let obj = member_expr!($span, $first);
member_expr!(@EXT, $span, obj, $($rest)* )
}};
(@EXT, $span:expr, $obj:expr, $first:ident . $($rest:tt)* ) => {{
use $crate::swc_ecma_ast::MemberProp;
let prop = MemberProp::Ident($crate::quote_ident!($span, stringify!($first)));
member_expr!(@EXT, $span, Box::new(Expr::Member(MemberExpr{
span: $crate::swc_common::DUMMY_SP,
obj: $obj,
prop,
})), $($rest)*)
}};
(@EXT, $span:expr, $obj:expr, $first:ident) => {{
use $crate::swc_ecma_ast::*;
let prop = MemberProp::Ident($crate::quote_ident!($span, stringify!($first)));
Box::new(Expr::Member(MemberExpr{
span: $crate::swc_common::DUMMY_SP,
obj: $obj,
prop,
}))
}};
}
#[cfg(test)]
mod tests {
use swc_common::DUMMY_SP as span;
use swc_ecma_ast::*;
use crate::drop_span;
#[test]
fn quote_member_expr() {
let expr: Box<Expr> = drop_span(member_expr!(span, Function.prototype.bind));
assert_eq!(
expr,
Box::new(Expr::Member(MemberExpr {
span,
obj: Box::new(Expr::Member(MemberExpr {
span,
obj: member_expr!(span, Function),
prop: MemberProp::Ident(quote_ident!(span, "prototype")),
})),
prop: MemberProp::Ident(quote_ident!(span, "bind")),
}))
);
}
}

View file

@ -0,0 +1,136 @@
use std::{borrow::Cow, fmt::Debug, hash::Hash};
use swc_common::EqIgnoreSpan;
use swc_ecma_ast::{Expr, MemberProp};
/// A newtype that will ignore Span while doing `eq` or `hash`.
pub struct NodeIgnoringSpan<'a, Node: ToOwned + Debug>(Cow<'a, Node>);
impl<'a, Node: ToOwned + Debug> Debug for NodeIgnoringSpan<'a, Node> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("NodeIgnoringSpan").field(&*self.0).finish()
}
}
impl<'a, Node: ToOwned + Debug> NodeIgnoringSpan<'a, Node> {
#[inline]
pub fn borrowed(expr: &'a Node) -> Self {
Self(Cow::Borrowed(expr))
}
#[inline]
pub fn owned(expr: <Node as ToOwned>::Owned) -> Self {
Self(Cow::Owned(expr))
}
}
impl<'a, Node: EqIgnoreSpan + ToOwned + Debug> PartialEq for NodeIgnoringSpan<'a, Node> {
fn eq(&self, other: &Self) -> bool {
self.0.eq_ignore_span(&other.0)
}
}
impl<'a, Node: EqIgnoreSpan + ToOwned + Debug> Eq for NodeIgnoringSpan<'a, Node> {}
// TODO: This is only a workaround for Expr. we need something like
// `hash_ignore_span` for each node in the end.
impl<'a> Hash for NodeIgnoringSpan<'a, Expr> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
// In pratice, most of cases/input we are dealing with are Expr::Member or
// Expr::Ident.
match &*self.0 {
Expr::Ident(i) => {
i.sym.hash(state);
}
Expr::Member(i) => {
{
NodeIgnoringSpan::borrowed(i.obj.as_ref()).hash(state);
}
if let MemberProp::Ident(prop) = &i.prop {
prop.sym.hash(state);
}
}
_ => {
// Other expression kinds would fallback to the same empty hash.
// So, they will spend linear time to do comparisons.
}
}
}
}
#[test]
fn test_hash_eq_ignore_span_expr_ref() {
use rustc_hash::FxHashSet;
use swc_common::{util::take::Take, Mark, DUMMY_SP};
use swc_ecma_ast::*;
use crate::{member_expr, quote_expr};
fn expr_ref(expr_ref: &Expr) -> NodeIgnoringSpan<Expr> {
NodeIgnoringSpan::borrowed(expr_ref)
}
testing::run_test(false, |_cm, _handler| {
Ident::within_ignored_ctxt(|| {
let dummy_sp = DUMMY_SP;
let meaningful_sp = dummy_sp.apply_mark(Mark::new());
let meaningful_ident_expr = Expr::Ident(Ident::new("foo".into(), meaningful_sp));
let dummy_ident_expr = Expr::Ident(Ident::new("foo".into(), dummy_sp));
let meaningful_member_expr = member_expr!(meaningful_sp, foo.bar);
let dummy_member_expr = member_expr!(dummy_sp, foo.bar);
let meaningful_null_expr = quote_expr!(meaningful_sp, null);
let dummy_null_expr = quote_expr!(dummy_sp, null);
let meaningful_array_expr = Box::new(Expr::Array(ArrayLit {
span: meaningful_sp,
elems: Default::default(),
}));
let dummy_array_expr = Box::new(Expr::Array(ArrayLit::dummy()));
// Should equal ignoring span and syntax context
assert_eq!(
expr_ref(&meaningful_ident_expr),
expr_ref(&dummy_ident_expr)
);
assert_eq!(
expr_ref(&meaningful_array_expr),
expr_ref(&dummy_array_expr)
);
let mut set = FxHashSet::from_iter([
expr_ref(&meaningful_ident_expr),
expr_ref(&meaningful_member_expr),
expr_ref(&meaningful_null_expr),
expr_ref(&meaningful_array_expr),
]);
// Should produce the same hash value ignoring span and syntax
assert!(set.contains(&expr_ref(&dummy_ident_expr)));
assert!(set.contains(&expr_ref(&dummy_member_expr)));
assert!(set.contains(&expr_ref(&dummy_null_expr)));
assert!(set.contains(&expr_ref(&dummy_array_expr)));
set.insert(expr_ref(&dummy_ident_expr));
set.insert(expr_ref(&dummy_member_expr));
set.insert(expr_ref(&dummy_null_expr));
set.insert(expr_ref(&dummy_array_expr));
assert_eq!(set.len(), 4);
// Should not equal ignoring span and syntax context
let dummy_ident_expr = Expr::Ident(Ident::new("baz".into(), dummy_sp));
let dummy_member_expr = member_expr!(dummy_sp, baz.bar);
let dummy_arrow_expr = Box::new(Expr::Arrow(ArrowExpr::dummy()));
assert!(!set.contains(&expr_ref(&dummy_ident_expr)));
assert!(!set.contains(&expr_ref(&dummy_member_expr)));
assert!(!set.contains(&expr_ref(&dummy_arrow_expr)));
});
Ok(())
})
.unwrap();
}

View file

@ -0,0 +1,187 @@
//! Module for parallel processing
use once_cell::sync::Lazy;
use swc_common::GLOBALS;
use swc_ecma_ast::*;
static CPU_COUNT: Lazy<usize> = Lazy::new(num_cpus::get);
pub fn cpu_count() -> usize {
*CPU_COUNT
}
pub trait Parallel: swc_common::sync::Send + swc_common::sync::Sync {
/// Used to create visitor.
fn create(&self) -> Self;
/// This can be called in anytime.
fn merge(&mut self, other: Self);
/// Invoked after visiting all [Stmt]s, possibly in parallel.
fn after_stmts(&mut self, _stmts: &mut Vec<Stmt>) {}
/// Invoked after visiting all [ModuleItem]s, possibly in parallel.
fn after_module_items(&mut self, _stmts: &mut Vec<ModuleItem>) {}
}
/// This is considered as a private type and it's NOT A PUBLIC API.
#[cfg(feature = "concurrent")]
#[allow(clippy::len_without_is_empty)]
pub trait Items:
rayon::iter::IntoParallelIterator<Iter = Self::ParIter> + IntoIterator<Item = Self::Elem>
{
type Elem: Send + Sync;
type ParIter: rayon::iter::ParallelIterator<Item = Self::Elem>
+ rayon::iter::IndexedParallelIterator;
fn len(&self) -> usize;
}
/// This is considered as a private type and it's NOT A PUBLIC API.
#[cfg(not(feature = "concurrent"))]
#[allow(clippy::len_without_is_empty)]
pub trait Items: IntoIterator<Item = Self::Elem> {
type Elem: Send + Sync;
fn len(&self) -> usize;
}
impl<T> Items for Vec<T>
where
T: Send + Sync,
{
type Elem = T;
#[cfg(feature = "concurrent")]
type ParIter = rayon::vec::IntoIter<T>;
fn len(&self) -> usize {
Vec::len(self)
}
}
impl<'a, T> Items for &'a mut Vec<T>
where
T: Send + Sync,
{
type Elem = &'a mut T;
#[cfg(feature = "concurrent")]
type ParIter = rayon::slice::IterMut<'a, T>;
fn len(&self) -> usize {
Vec::len(self)
}
}
impl<'a, T> Items for &'a mut [T]
where
T: Send + Sync,
{
type Elem = &'a mut T;
#[cfg(feature = "concurrent")]
type ParIter = rayon::slice::IterMut<'a, T>;
fn len(&self) -> usize {
<[T]>::len(self)
}
}
impl<'a, T> Items for &'a [T]
where
T: Send + Sync,
{
type Elem = &'a T;
#[cfg(feature = "concurrent")]
type ParIter = rayon::slice::Iter<'a, T>;
fn len(&self) -> usize {
<[T]>::len(self)
}
}
pub trait ParallelExt: Parallel {
/// Invoke `op` in parallel, if `swc_ecma_utils` is compiled with
/// concurrent feature enabled and `nodes.len()` is bigger than threshold.
///
///
/// This configures [GLOBALS], while not configuring [HANDLER] nor [HELPERS]
fn maybe_par<I, F>(&mut self, threshold: usize, nodes: I, op: F)
where
I: Items,
F: Send + Sync + Fn(&mut Self, I::Elem),
{
self.maybe_par_idx(threshold, nodes, |v, _, n| op(v, n))
}
/// Invoke `op` in parallel, if `swc_ecma_utils` is compiled with
/// concurrent feature enabled and `nodes.len()` is bigger than threshold.
///
///
/// This configures [GLOBALS], while not configuring [HANDLER] nor [HELPERS]
fn maybe_par_idx<I, F>(&mut self, threshold: usize, nodes: I, op: F)
where
I: Items,
F: Send + Sync + Fn(&mut Self, usize, I::Elem);
}
#[cfg(feature = "concurrent")]
impl<T> ParallelExt for T
where
T: Parallel,
{
fn maybe_par_idx<I, F>(&mut self, threshold: usize, nodes: I, op: F)
where
I: Items,
F: Send + Sync + Fn(&mut Self, usize, I::Elem),
{
if nodes.len() >= threshold || option_env!("SWC_FORCE_CONCURRENT") == Some("1") {
GLOBALS.with(|globals| {
use rayon::prelude::*;
let visitor = nodes
.into_par_iter()
.enumerate()
.map(|(idx, node)| {
GLOBALS.set(globals, || {
let mut visitor = Parallel::create(&*self);
op(&mut visitor, idx, node);
visitor
})
})
.reduce(
|| Parallel::create(&*self),
|mut a, b| {
Parallel::merge(&mut a, b);
a
},
);
Parallel::merge(self, visitor);
});
return;
}
for (idx, n) in nodes.into_iter().enumerate() {
op(self, idx, n);
}
}
}
#[cfg(not(feature = "concurrent"))]
impl<T> ParallelExt for T
where
T: Parallel,
{
fn maybe_par_idx<I, F>(&mut self, _threshold: usize, nodes: I, op: F)
where
I: Items,
F: Send + Sync + Fn(&mut Self, usize, I::Elem),
{
for (idx, n) in nodes.into_iter().enumerate() {
op(self, idx, n);
}
}
}

View file

@ -0,0 +1,110 @@
use std::ops::Not;
use self::Value::{Known, Unknown};
/// Runtime value.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Value<T> {
Known(T),
/// Not determined at compile time.`
Unknown,
}
/// Type of value.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Type {
Undefined,
Null,
Bool,
Str,
Symbol,
Num,
Obj,
}
impl Value<Type> {
pub fn casted_to_number_on_add(self) -> bool {
match self {
Known(Type::Bool) | Known(Type::Null) | Known(Type::Num) | Known(Type::Undefined) => {
true
}
_ => false,
}
}
}
/// Value could not be determined
pub struct UnknownError;
// impl<T> Try for Value<T> {
// type Ok = T;
// type Error = UnknownError;
// fn from_ok(t: T) -> Self {
// Known(t)
// }
// fn from_error(_: UnknownError) -> Self {
// Unknown
// }
// fn into_result(self) -> Result<T, UnknownError> {
// match self {
// Known(t) => Ok(t),
// Unknown => Err(UnknownError),
// }
// }
// }
impl<T> Value<T> {
pub fn into_result(self) -> Result<T, UnknownError> {
match self {
Known(v) => Ok(v),
Unknown => Err(UnknownError),
}
}
}
impl<T> Value<T> {
/// Returns true if the value is not known.
pub fn is_unknown(&self) -> bool {
matches!(*self, Unknown)
}
/// Returns true if the value is known.
pub fn is_known(&self) -> bool {
matches!(*self, Known(..))
}
}
impl Value<bool> {
pub fn and(self, other: Self) -> Self {
match self {
Known(true) => other,
Known(false) => Known(false),
Unknown => match other {
Known(false) => Known(false),
_ => Unknown,
},
}
}
pub fn or(self, other: Self) -> Self {
match self {
Known(true) => Known(true),
Known(false) => other,
Unknown => match other {
Known(true) => Known(true),
_ => Unknown,
},
}
}
}
impl Not for Value<bool> {
type Output = Self;
fn not(self) -> Self {
match self {
Value::Known(b) => Value::Known(!b),
Value::Unknown => Value::Unknown,
}
}
}

View file

@ -0,0 +1,34 @@
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
use crate::ident::IdentLike;
/// This collects variables bindings while ignoring if it's nested in
/// expression.
pub struct VarCollector<'a, I: IdentLike> {
pub to: &'a mut Vec<I>,
}
impl<'a, I: IdentLike> Visit for VarCollector<'a, I> {
noop_visit_type!();
fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
fn visit_constructor(&mut self, _: &Constructor) {}
fn visit_expr(&mut self, _: &Expr) {}
fn visit_function(&mut self, _: &Function) {}
fn visit_key_value_pat_prop(&mut self, node: &KeyValuePatProp) {
node.value.visit_with(self);
}
fn visit_ident(&mut self, i: &Ident) {
self.to.push(I::from_ident(i))
}
fn visit_var_declarator(&mut self, node: &VarDeclarator) {
node.name.visit_with(self);
}
}