use swc_core::{
common::{comments::Comments, util::take::Take, SourceMapper, DUMMY_SP},
ecma::{
ast::*,
utils::IsDirective,
visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith},
},
};
use tracing::instrument;
use crate::{
create_instrumentation_visitor, instrumentation_counter_helper,
instrumentation_stmt_counter_helper, instrumentation_visitor, InstrumentOptions,
};
create_instrumentation_visitor!(CoverageVisitor { file_path: String });
pub fn create_coverage_instrumentation_visitor<C: Clone + Comments, S: SourceMapper>(
source_map: std::sync::Arc<S>,
comments: C,
instrument_options: InstrumentOptions,
filename: String,
) -> CoverageVisitor<C, S> {
crate::create_coverage_fn_ident(&filename);
let mut cov = crate::SourceCoverage::new(filename.to_string(), instrument_options.report_logic);
cov.set_input_source_map(&instrument_options.input_source_map);
CoverageVisitor::new(
source_map,
comments.clone(),
std::rc::Rc::new(std::cell::RefCell::new(cov)),
instrument_options,
vec![],
None,
filename,
)
}
impl<C: Clone + Comments, S: SourceMapper> CoverageVisitor<C, S> {
instrumentation_counter_helper!();
instrumentation_stmt_counter_helper!();
fn is_instrumented_already(&self) -> bool {
return false;
}
fn get_coverage_templates(&mut self) -> (Stmt, Stmt) {
self.cov.borrow_mut().freeze();
let coverage_global_scope = "this";
let coverage_global_scope_func = true;
let gv_template = if coverage_global_scope_func {
let is_function_binding_scope = false;
if is_function_binding_scope {
unimplemented!("");
} else {
crate::create_global_stmt_template(coverage_global_scope)
}
} else {
unimplemented!("");
};
let coverage_template = crate::create_coverage_fn_decl(
&self.instrument_options.coverage_variable,
gv_template,
&self.cov_fn_ident,
&self.file_path,
self.cov.borrow().as_ref(),
&self.comments,
self.instrument_options.debug_initial_coverage_comment,
);
let call_coverage_template_stmt = Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr: Box::new(Expr::Call(CallExpr {
callee: Callee::Expr(Box::new(Expr::Ident(self.cov_fn_ident.clone()))),
..CallExpr::dummy()
})),
});
(coverage_template, call_coverage_template_stmt)
}
}
impl<C: Clone + Comments, S: SourceMapper> VisitMut for CoverageVisitor<C, S> {
instrumentation_visitor!();
#[instrument(skip_all, fields(node = %self.print_node()))]
fn visit_mut_program(&mut self, program: &mut Program) {
self.nodes.push(crate::Node::Program);
if crate::hint_comments::should_ignore_file(&self.comments, program) {
return;
}
if self.is_instrumented_already() {
return;
}
program.visit_mut_children_with(self);
self.nodes.pop();
}
#[instrument(skip_all, fields(node = %self.print_node()))]
fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
if self.is_instrumented_already() {
return;
}
let root_exists = match self.nodes.get(0) {
Some(node) => node == &crate::Node::Program,
_ => false,
};
if !root_exists {
let mut new_nodes = vec![crate::Node::Program];
new_nodes.extend(self.nodes.drain(..));
self.nodes = new_nodes;
}
let mut new_items = vec![];
for mut item in items.drain(..) {
if let ModuleItem::Stmt(stmt) = &item {
if stmt.directive_continue() {
new_items.push(item);
continue;
}
}
let (old, _ignore_current) = match &mut item {
ModuleItem::ModuleDecl(decl) => self.on_enter(decl),
ModuleItem::Stmt(stmt) => self.on_enter(stmt),
};
item.visit_mut_children_with(self);
new_items.extend(self.before.drain(..).map(|v| ModuleItem::Stmt(v)));
new_items.push(item);
self.on_exit(old);
}
*items = new_items;
let (coverage_template, call_coverage_template_stmt) = self.get_coverage_templates();
if items.len() >= 1 {
items.insert(1, ModuleItem::Stmt(coverage_template));
items.insert(2, ModuleItem::Stmt(call_coverage_template_stmt));
} else {
items.push(ModuleItem::Stmt(coverage_template));
items.push(ModuleItem::Stmt(call_coverage_template_stmt));
}
if !root_exists {
self.nodes.pop();
}
}
#[instrument(skip_all, fields(node = %self.print_node()))]
fn visit_mut_script(&mut self, items: &mut Script) {
if self.is_instrumented_already() {
return;
}
let mut new_items = vec![];
for mut item in items.body.drain(..) {
item.visit_mut_children_with(self);
new_items.extend(self.before.drain(..));
new_items.push(item);
}
items.body = new_items;
let (coverage_template, call_coverage_template_stmt) = self.get_coverage_templates();
items.body.insert(0, coverage_template);
items.body.insert(1, call_coverage_template_stmt);
}
#[instrument(skip_all, fields(node = %self.print_node()))]
fn visit_mut_export_default_decl(&mut self, export_default_decl: &mut ExportDefaultDecl) {
let (old, ignore_current) = self.on_enter(export_default_decl);
match ignore_current {
Some(crate::hint_comments::IgnoreScope::Next) => {}
_ => {
export_default_decl.visit_mut_children_with(self);
}
}
self.on_exit(old);
}
#[instrument(skip_all, fields(node = %self.print_node()))]
fn visit_mut_export_decl(&mut self, export_named_decl: &mut ExportDecl) {
let (old, ignore_current) = self.on_enter(export_named_decl);
match ignore_current {
Some(crate::hint_comments::IgnoreScope::Next) => {}
_ => {
export_named_decl.visit_mut_children_with(self);
}
}
self.on_exit(old);
}
#[instrument(skip_all, fields(node = %self.print_node()))]
fn visit_mut_debugger_stmt(&mut self, debugger_stmt: &mut DebuggerStmt) {
let (old, ignore_current) = self.on_enter(debugger_stmt);
match ignore_current {
Some(crate::hint_comments::IgnoreScope::Next) => {}
_ => {
debugger_stmt.visit_mut_children_with(self);
}
}
self.on_exit(old);
}
}