swc_coverage_instrument/visitors/
coverage_visitor.rs1use swc_core::{
2 common::{comments::Comments, util::take::Take, SourceMapper, DUMMY_SP},
3 ecma::{
4 ast::*,
5 utils::IsDirective,
6 visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith},
7 },
8};
9use tracing::instrument;
10
11use crate::{
12 create_instrumentation_visitor, instrumentation_counter_helper,
13 instrumentation_stmt_counter_helper, instrumentation_visitor, InstrumentOptions,
14};
15
16create_instrumentation_visitor!(CoverageVisitor { file_path: String });
17
18pub fn create_coverage_instrumentation_visitor<C: Clone + Comments, S: SourceMapper>(
21 source_map: std::sync::Arc<S>,
22 comments: C,
23 instrument_options: InstrumentOptions,
24 filename: String,
25) -> CoverageVisitor<C, S> {
26 crate::create_coverage_fn_ident(&filename);
28
29 let mut cov = crate::SourceCoverage::new(filename.to_string(), instrument_options.report_logic);
30 cov.set_input_source_map(&instrument_options.input_source_map);
31
32 CoverageVisitor::new(
33 source_map,
34 comments.clone(),
35 std::rc::Rc::new(std::cell::RefCell::new(cov)),
36 instrument_options,
37 vec![],
38 None,
39 filename,
40 )
41}
42
43impl<C: Clone + Comments, S: SourceMapper> CoverageVisitor<C, S> {
44 instrumentation_counter_helper!();
45 instrumentation_stmt_counter_helper!();
46
47 fn is_instrumented_already(&self) -> bool {
50 return false;
51 }
52
53 fn get_coverage_templates(&mut self) -> (Stmt, Stmt) {
55 self.cov.borrow_mut().freeze();
56
57 let coverage_global_scope = "this";
59 let coverage_global_scope_func = true;
61
62 let gv_template = if coverage_global_scope_func {
63 let is_function_binding_scope = false;
65
66 if is_function_binding_scope {
67 unimplemented!("");
75 } else {
76 crate::create_global_stmt_template(coverage_global_scope)
77 }
78 } else {
79 unimplemented!("");
80 };
86
87 let coverage_template = crate::create_coverage_fn_decl(
88 &self.instrument_options.coverage_variable,
89 gv_template,
90 &self.cov_fn_ident,
91 &self.file_path,
92 self.cov.borrow().as_ref(),
93 &self.comments,
94 self.instrument_options.debug_initial_coverage_comment,
95 );
96
97 let call_coverage_template_stmt = Stmt::Expr(ExprStmt {
99 span: DUMMY_SP,
100 expr: Box::new(Expr::Call(CallExpr {
101 callee: Callee::Expr(Box::new(Expr::Ident(self.cov_fn_ident.clone()))),
102 ..CallExpr::dummy()
103 })),
104 });
105
106 (coverage_template, call_coverage_template_stmt)
107 }
108}
109
110impl<C: Clone + Comments, S: SourceMapper> VisitMut for CoverageVisitor<C, S> {
111 instrumentation_visitor!();
112
113 #[instrument(skip_all, fields(node = %self.print_node()))]
114 fn visit_mut_program(&mut self, program: &mut Program) {
115 self.nodes.push(crate::Node::Program);
116 if crate::hint_comments::should_ignore_file(&self.comments, program) {
117 return;
118 }
119
120 if self.is_instrumented_already() {
121 return;
122 }
123
124 program.visit_mut_children_with(self);
125 self.nodes.pop();
126 }
127
128 #[instrument(skip_all, fields(node = %self.print_node()))]
129 fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
130 if self.is_instrumented_already() {
131 return;
132 }
133
134 let root_exists = match self.nodes.get(0) {
135 Some(node) => node == &crate::Node::Program,
136 _ => false,
137 };
138
139 if !root_exists {
143 let mut new_nodes = vec![crate::Node::Program];
144 new_nodes.extend(self.nodes.drain(..));
145 self.nodes = new_nodes;
146 }
147
148 let mut new_items = vec![];
150 for mut item in items.drain(..) {
151 if let ModuleItem::Stmt(stmt) = &item {
152 if stmt.directive_continue() {
154 new_items.push(item);
155 continue;
156 }
157 }
158
159 let (old, _ignore_current) = match &mut item {
160 ModuleItem::ModuleDecl(decl) => self.on_enter(decl),
161 ModuleItem::Stmt(stmt) => self.on_enter(stmt),
162 };
163
164 if let ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) = &item {
170 if let Decl::Var(var_decl) = &export_decl.decl {
171 if var_decl.kind == VarDeclKind::Const {
172 self.mark_prepend_stmt_counter(&export_decl.span);
173 }
174 }
175 }
176
177 item.visit_mut_children_with(self);
178
179 new_items.extend(self.before.drain(..).map(|v| ModuleItem::Stmt(v)));
180 new_items.push(item);
181 self.on_exit(old);
182 }
183 *items = new_items;
184
185 let (coverage_template, call_coverage_template_stmt) = self.get_coverage_templates();
186
187 if items.len() >= 1 {
189 items.insert(1, ModuleItem::Stmt(coverage_template));
190 items.insert(2, ModuleItem::Stmt(call_coverage_template_stmt));
191 } else {
192 items.push(ModuleItem::Stmt(coverage_template));
193 items.push(ModuleItem::Stmt(call_coverage_template_stmt));
194 }
195
196 if !root_exists {
197 self.nodes.pop();
198 }
199 }
200
201 #[instrument(skip_all, fields(node = %self.print_node()))]
202 fn visit_mut_script(&mut self, items: &mut Script) {
203 if self.is_instrumented_already() {
204 return;
205 }
206
207 let mut new_items = vec![];
208 for mut item in items.body.drain(..) {
209 item.visit_mut_children_with(self);
210 new_items.extend(self.before.drain(..));
211 new_items.push(item);
212 }
213 items.body = new_items;
214
215 let (coverage_template, call_coverage_template_stmt) = self.get_coverage_templates();
216
217 items.body.insert(0, coverage_template);
219 items.body.insert(1, call_coverage_template_stmt);
220 }
221
222 #[instrument(skip_all, fields(node = %self.print_node()))]
224 fn visit_mut_export_default_decl(&mut self, export_default_decl: &mut ExportDefaultDecl) {
225 let (old, ignore_current) = self.on_enter(export_default_decl);
226 match ignore_current {
227 Some(crate::hint_comments::IgnoreScope::Next) => {}
228 _ => {
229 export_default_decl.visit_mut_children_with(self);
231 }
232 }
233 self.on_exit(old);
234 }
235
236 #[instrument(skip_all, fields(node = %self.print_node()))]
238 fn visit_mut_export_decl(&mut self, export_named_decl: &mut ExportDecl) {
239 let (old, ignore_current) = self.on_enter(export_named_decl);
240 match ignore_current {
241 Some(crate::hint_comments::IgnoreScope::Next) => {}
242 _ => {
243 export_named_decl.visit_mut_children_with(self);
245 }
246 }
247 self.on_exit(old);
248 }
249
250 #[instrument(skip_all, fields(node = %self.print_node()))]
252 fn visit_mut_debugger_stmt(&mut self, debugger_stmt: &mut DebuggerStmt) {
253 let (old, ignore_current) = self.on_enter(debugger_stmt);
254 match ignore_current {
255 Some(crate::hint_comments::IgnoreScope::Next) => {}
256 _ => {
257 debugger_stmt.visit_mut_children_with(self);
258 }
259 }
260 self.on_exit(old);
261 }
262}