use std::collections::HashMap;
use hermes_ast::context::{GCLock, NodeRc};
use hermes_ast::node::{CatchClause, Node, VariableDeclaration};
use hermes_ast::visitor::Visitor;
use hermes_ast::NodeId;
use crate::dump_context::push_str;
use crate::keywords::Keywords;
pub type ScopeDecls = Vec<NodeRc>;
pub struct DeclCollector {
scopes: HashMap<NodeId, ScopeDecls>,
scoped_func_decls: Vec<NodeRc>,
}
impl DeclCollector {
pub fn run<'ast, 'g_ast, 'g_ctx, 'k>(
root: &'ast Node<'ast>,
gc: &'ast GCLock<'g_ast, 'g_ctx>,
kw: &'k Keywords,
recursion_depth: u32,
recursion_depth_exceeded: &mut dyn FnMut(&'ast Node<'ast>),
) -> DeclCollector {
let mut collector = Collector {
gc,
kw,
scopes: HashMap::new(),
scoped_func_decls: Vec::new(),
scope_stack: Vec::new(),
remaining_depth: recursion_depth,
on_depth_exceeded: recursion_depth_exceeded,
};
collector.run_impl(root);
debug_assert!(
collector.scope_stack.is_empty(),
"run_impl must close every scope it opens"
);
DeclCollector {
scopes: collector.scopes,
scoped_func_decls: collector.scoped_func_decls,
}
}
pub fn scope_decls_for_node(&self, node_id: NodeId) -> Option<&ScopeDecls> {
self.scopes.get(&node_id)
}
pub fn scoped_func_decls(&self) -> &[NodeRc] {
&self.scoped_func_decls
}
pub fn dump(&self, out: &mut Vec<u8>, gc: &GCLock, indent: u32) {
for (node_id, decls) in &self.scopes {
out.resize(out.len() + indent as usize, b' ');
push_str(out, "NodeId(");
push_str(out, &node_id.0.to_string());
push_str(out, "):");
for n in decls {
let node = n.node(gc);
out.push(b' ');
push_str(out, node.node_type_str());
push_str(out, "[NodeId(");
push_str(out, &node.node_id().0.to_string());
push_str(out, ")]");
}
out.push(b'\n');
}
}
}
struct Collector<'ast, 'g_ast, 'g_ctx, 'cb, 'k> {
gc: &'ast GCLock<'g_ast, 'g_ctx>,
kw: &'k Keywords,
scopes: HashMap<NodeId, ScopeDecls>,
scoped_func_decls: Vec<NodeRc>,
scope_stack: Vec<ScopeDecls>,
remaining_depth: u32,
on_depth_exceeded: &'cb mut dyn FnMut(&'ast Node<'ast>),
}
impl<'ast, 'g_ast, 'g_ctx, 'cb, 'k> Collector<'ast, 'g_ast, 'g_ctx, 'cb, 'k> {
fn run_impl(&mut self, root: &'ast Node<'ast>) {
match root {
Node::FunctionDeclaration(f) => {
self.new_scope();
let body = f.body.as_block_statement().expect(
"FunctionDeclaration body is always a BlockStatement",
);
for c in body.body.iter() {
self.visit_node(c);
}
self.close_scope(root);
}
Node::FunctionExpression(f) => {
self.new_scope();
let body = f.body.as_block_statement().expect(
"FunctionExpression body is always a BlockStatement",
);
for c in body.body.iter() {
self.visit_node(c);
}
self.close_scope(root);
}
Node::ArrowFunctionExpression(f) => {
self.new_scope();
if let Some(body) = f.body.as_block_statement() {
for c in body.body.iter() {
self.visit_node(c);
}
} else {
root.visit_children(self);
}
self.close_scope(root);
}
_ => {
self.new_scope();
root.visit_children(self);
self.close_scope(root);
}
}
}
fn inc_recursion_depth(&mut self, node: &'ast Node<'ast>) -> bool {
if self.remaining_depth == 0 {
return false;
}
self.remaining_depth -= 1;
if self.remaining_depth == 0 {
(self.on_depth_exceeded)(node);
return false;
}
true
}
fn dec_recursion_depth(&mut self) {
if self.remaining_depth != 0 {
self.remaining_depth += 1;
}
}
fn add_to_func(&mut self, node: &'ast Node<'ast>) {
let rc = NodeRc::from_node(self.gc, node);
self.scope_stack
.first_mut()
.expect("missing function scope")
.push(rc);
}
fn add_to_cur(&mut self, node: &'ast Node<'ast>) {
let rc = NodeRc::from_node(self.gc, node);
self.scope_stack
.last_mut()
.expect("no current scope")
.push(rc);
}
fn new_scope(&mut self) {
self.scope_stack.push(Vec::new());
}
fn close_scope(&mut self, node: &'ast Node<'ast>) {
let decls = self.scope_stack.pop().expect("no scope to close");
if !decls.is_empty() {
let prev = self.scopes.insert(node.node_id(), decls);
debug_assert!(prev.is_none(), "tried to collect same node twice");
}
}
fn visit_variable_declaration(
&mut self,
node: &'ast Node<'ast>,
vd: &'ast VariableDeclaration<'ast>,
) {
if vd.kind.get() == self.kw.ident_var {
self.add_to_func(node);
} else {
self.add_to_cur(node);
}
node.visit_children(self);
}
fn visit_import_declaration(&mut self, node: &'ast Node<'ast>) {
self.add_to_cur(node);
node.visit_children(self);
}
fn visit_type_alias(&mut self, node: &'ast Node<'ast>) {
self.add_to_cur(node);
node.visit_children(self);
}
fn visit_ts_type_alias_declaration(&mut self, node: &'ast Node<'ast>) {
self.add_to_cur(node);
node.visit_children(self);
}
fn visit_function_declaration(&mut self, node: &'ast Node<'ast>) {
self.add_to_cur(node);
if self.scope_stack.len() > 1 {
self.scoped_func_decls.push(NodeRc::from_node(self.gc, node));
}
}
fn visit_scope_creating(&mut self, node: &'ast Node<'ast>) {
self.new_scope();
node.visit_children(self);
self.close_scope(node);
}
fn visit_catch_clause(
&mut self,
node: &'ast Node<'ast>,
cc: &'ast CatchClause<'ast>,
) {
self.new_scope();
if let Some(param) = cc.param {
self.add_to_cur(node);
self.visit_node(param);
}
self.visit_node(cc.body);
self.close_scope(node);
}
fn dispatch(&mut self, node: &'ast Node<'ast>) {
match node {
Node::VariableDeclaration(vd) => {
self.visit_variable_declaration(node, vd)
}
Node::ClassDeclaration(_) => self.add_to_cur(node),
Node::ClassExpression(_) => {}
Node::ImportDeclaration(_) => self.visit_import_declaration(node),
Node::TypeAlias(_) => self.visit_type_alias(node),
Node::InterfaceDeclaration(_) => {}
Node::TSTypeAliasDeclaration(_) => {
self.visit_ts_type_alias_declaration(node)
}
Node::TSInterfaceDeclaration(_) => {}
Node::FunctionDeclaration(_) => {
self.visit_function_declaration(node)
}
Node::FunctionExpression(_) => {}
Node::ArrowFunctionExpression(_) => {}
Node::BlockStatement(_) => self.visit_scope_creating(node),
Node::ForStatement(_) => self.visit_scope_creating(node),
Node::ForInStatement(_) => self.visit_scope_creating(node),
Node::ForOfStatement(_) => self.visit_scope_creating(node),
Node::SwitchStatement(_) => self.visit_scope_creating(node),
Node::CatchClause(cc) => self.visit_catch_clause(node, cc),
Node::BinaryExpression(_) => {}
Node::AssignmentExpression(_) => {}
_ => node.visit_children(self),
}
}
}
impl<'ast, 'g_ast, 'g_ctx, 'cb, 'k> Visitor<'ast>
for Collector<'ast, 'g_ast, 'g_ctx, 'cb, 'k>
{
fn visit_node(&mut self, node: &'ast Node<'ast>) {
if !self.inc_recursion_depth(node) {
return;
}
self.dispatch(node);
self.dec_recursion_depth();
}
}