foundry_evm_coverage/
inspector.rs1use crate::{HitMap, HitMaps};
2use alloy_primitives::B256;
3use revm::{
4 Inspector,
5 interpreter::{Interpreter, interpreter_types::Jumps},
6};
7use std::ptr::NonNull;
8
9#[derive(Clone, Debug)]
11pub struct LineCoverageCollector {
12 current_map: NonNull<HitMap>,
16 current_hash: B256,
17
18 maps: HitMaps,
19}
20
21unsafe impl Send for LineCoverageCollector {}
23unsafe impl Sync for LineCoverageCollector {}
24
25impl Default for LineCoverageCollector {
26 fn default() -> Self {
27 Self {
28 current_map: NonNull::dangling(),
29 current_hash: B256::ZERO,
30 maps: Default::default(),
31 }
32 }
33}
34
35impl<CTX> Inspector<CTX> for LineCoverageCollector {
36 fn initialize_interp(&mut self, interpreter: &mut Interpreter, _context: &mut CTX) {
37 let map = self.get_or_insert_map(interpreter);
38 map.reserve(8192.min(interpreter.bytecode.len()));
40 }
41
42 fn step(&mut self, interpreter: &mut Interpreter, _context: &mut CTX) {
43 let map = self.get_or_insert_map(interpreter);
44 map.hit(interpreter.bytecode.pc() as u32);
45 }
46}
47
48impl LineCoverageCollector {
49 pub fn finish(self) -> HitMaps {
51 self.maps
52 }
53
54 #[inline]
59 fn get_or_insert_map(&mut self, interpreter: &mut Interpreter) -> &mut HitMap {
60 let hash = interpreter.bytecode.get_or_calculate_hash();
61 if self.current_hash != *hash {
62 self.insert_map(interpreter);
63 }
64 unsafe { self.current_map.as_mut() }
66 }
67
68 #[cold]
69 #[inline(never)]
70 fn insert_map(&mut self, interpreter: &mut Interpreter) {
71 let hash = interpreter.bytecode.hash().unwrap();
72 self.current_hash = hash;
73 self.current_map = self
75 .maps
76 .entry(hash)
77 .or_insert_with(|| HitMap::new(interpreter.bytecode.original_bytes()))
78 .into();
79 }
80}