use swc_common::Mark;
use swc_ecma_ast::*;
use self::scope::{Scope, ScopeKind};
mod reverse_map;
pub(super) mod scope;
#[derive(Debug, Default)]
pub(super) struct Analyzer {
pub(super) has_eval: bool,
pub(super) top_level_mark: Mark,
pub(super) is_pat_decl: bool,
pub(super) var_belong_to_fn_scope: bool,
pub(super) in_catch_params: bool,
pub(super) scope: Scope,
pub(super) hoisted_vars: Vec<Id>,
pub(super) skip_first_fn_or_class_decl: bool,
pub(super) is_first_node: bool,
pub(super) mangle: bool,
}
impl Analyzer {
pub(super) fn new(
has_eval: bool,
top_level_mark: Mark,
skip_first_fn_or_class_decl: bool,
mangle: bool,
) -> Self {
Self {
has_eval,
top_level_mark,
skip_first_fn_or_class_decl,
is_first_node: true,
hoisted_vars: Vec::with_capacity(32),
mangle,
..Default::default()
}
}
pub(super) fn add_decl(&mut self, id: Id, belong_to_fn_scope: bool) {
if belong_to_fn_scope {
match self.scope.kind {
ScopeKind::Fn => {
self.scope.add_decl(&id, self.has_eval, self.top_level_mark);
}
ScopeKind::Block => self.hoisted_vars.push(id),
}
} else {
self.scope.add_decl(&id, self.has_eval, self.top_level_mark);
}
}
fn reserve_decl(&mut self, len: usize, belong_to_fn_scope: bool) {
if belong_to_fn_scope {
match self.scope.kind {
ScopeKind::Fn => {
self.scope.reserve_decl(len);
}
ScopeKind::Block => {
self.hoisted_vars.reserve(len);
}
}
} else {
self.scope.reserve_decl(len);
}
}
pub(super) fn add_usage(&mut self, id: Id) {
self.scope.add_usage(id);
}
fn reserve_usage(&mut self, len: usize) {
self.scope.reserve_usage(len);
}
fn enter_scope(&mut self, kind: ScopeKind) -> Self {
let mut analyer = Analyzer {
has_eval: self.has_eval,
top_level_mark: self.top_level_mark,
is_pat_decl: self.is_pat_decl,
var_belong_to_fn_scope: false,
in_catch_params: false,
scope: Scope {
kind,
..Default::default()
},
skip_first_fn_or_class_decl: false,
is_first_node: false,
hoisted_vars: Default::default(),
mangle: self.mangle,
};
std::mem::swap(self, &mut analyer);
analyer }
pub(super) fn enter_fn_scope(&mut self) -> Self {
self.enter_scope(ScopeKind::Fn)
}
pub(super) fn enter_block_scope(&mut self) -> Self {
self.enter_scope(ScopeKind::Block)
}
pub(super) fn exit_scope(&mut self, mut v: Self) {
std::mem::swap(self, &mut v);
if !v.hoisted_vars.is_empty() {
debug_assert!(matches!(v.scope.kind, ScopeKind::Block));
self.reserve_usage(v.hoisted_vars.len());
v.hoisted_vars.clone().into_iter().for_each(|id| {
v.add_usage(id);
});
match self.scope.kind {
ScopeKind::Fn => {
self.reserve_decl(v.hoisted_vars.len(), true);
v.hoisted_vars
.into_iter()
.for_each(|id| self.add_decl(id, true));
}
ScopeKind::Block => {
self.hoisted_vars.extend(v.hoisted_vars);
}
}
}
self.scope.children.push(v.scope);
}
pub(super) fn handle_binding_ident(&mut self, node: &BindingIdent) {
if self.is_pat_decl {
self.add_decl(node.to_id(), self.var_belong_to_fn_scope)
} else {
self.add_usage(node.to_id())
}
}
pub(super) fn handle_class_decl(&mut self, node: &ClassDecl) {
if self.is_first_node && self.skip_first_fn_or_class_decl {
self.is_first_node = false;
return;
}
self.is_first_node = false;
self.add_decl(node.ident.to_id(), false);
}
pub(super) fn handle_class_expr(&mut self, node: &ClassExpr) {
if let Some(id) = &node.ident {
self.add_decl(id.to_id(), false);
}
}
pub(super) fn handle_fn_expr(&mut self, node: &FnExpr) {
if let Some(id) = &node.ident {
self.add_decl(id.to_id(), true);
}
}
pub(super) fn handle_export_named_specifier(&mut self, n: &ExportNamedSpecifier) {
match &n.orig {
ModuleExportName::Ident(orig) => {
self.add_usage(orig.to_id());
}
ModuleExportName::Str(..) => {}
#[cfg(swc_ast_unknown)]
_ => {}
};
}
pub(super) fn handle_expr(&mut self, e: &Expr) {
if let Expr::Ident(i) = e {
self.add_usage(i.to_id());
}
}
pub(super) fn handle_prop(&mut self, node: &Prop) {
if let Prop::Shorthand(i) = node {
self.add_usage(i.to_id());
}
}
}