1pub use cov::CoverageResult;
4pub use debugger::{
5 BreakpointKind, DebugSession, DebugSessionHandler, set_debug_session, with_debug_session,
6};
7
8mod cov;
9mod debugger;
10mod instrument;
11
12use std::ops::DerefMut;
13use std::sync::Arc;
14
15use debugger::BreakpointInstr;
16use parking_lot::Mutex;
17use tinymist_std::{error::prelude::*, hash::FxHashMap};
18use tinymist_world::package::PackageSpec;
19use tinymist_world::{CompilerFeat, CompilerWorld, print_diagnostics};
20use typst::Library;
21use typst::diag::EcoString;
22use typst::syntax::package::PackageVersion;
23use typst::utils::LazyHash;
24
25use cov::*;
26use instrument::InstrumentWorld;
27
28pub fn collect_coverage<D: typst::Document, F: CompilerFeat>(
30 base: &CompilerWorld<F>,
31) -> Result<CoverageResult> {
32 let (cov, result) = with_cov(base, |instr| {
33 if let Err(e) = typst::compile::<D>(&instr).output {
34 print_diagnostics(instr, e.iter(), tinymist_world::DiagnosticFormat::Human)
35 .context_ut("failed to print diagnostics")?;
36 bail!("");
37 }
38
39 Ok(())
40 });
41
42 result?;
43 cov
44}
45
46pub fn with_cov<F: CompilerFeat, T>(
48 base: &CompilerWorld<F>,
49 mut f: impl FnMut(&InstrumentWorld<F, CovInstr>) -> Result<T>,
50) -> (Result<CoverageResult>, Result<T>) {
51 let instr = InstrumentWorld {
52 base,
53 library: instrument_library(&base.library),
54 instr: CovInstr::default(),
55 instrumented: Mutex::new(FxHashMap::default()),
56 };
57
58 let _cov_lock = cov::COVERAGE_LOCK.lock();
59
60 let result = f(&instr);
61
62 let meta = std::mem::take(instr.instr.map.lock().deref_mut());
63 let CoverageMap { regions, .. } = std::mem::take(cov::COVERAGE_MAP.lock().deref_mut());
64
65 (Ok(CoverageResult { meta, regions }), result)
66}
67
68pub type DebuggerWorld<'a, F> = InstrumentWorld<'a, F, BreakpointInstr>;
70pub fn instr_breakpoints<F: CompilerFeat>(base: &CompilerWorld<F>) -> DebuggerWorld<'_, F> {
72 InstrumentWorld {
73 base,
74 library: instrument_library(&base.library),
75 instr: BreakpointInstr::default(),
76 instrumented: Mutex::new(FxHashMap::default()),
77 }
78}
79
80#[comemo::memoize]
81fn instrument_library(library: &Arc<LazyHash<Library>>) -> Arc<LazyHash<Library>> {
82 use debugger::breakpoints::*;
83
84 let mut library = library.as_ref().clone();
85
86 let scope = library.global.scope_mut();
87 scope.define_func::<__cov_pc>();
88 scope.define_func::<__breakpoint_call_start>();
89 scope.define_func::<__breakpoint_call_end>();
90 scope.define_func::<__breakpoint_function>();
91 scope.define_func::<__breakpoint_break>();
92 scope.define_func::<__breakpoint_continue>();
93 scope.define_func::<__breakpoint_return>();
94 scope.define_func::<__breakpoint_block_start>();
95 scope.define_func::<__breakpoint_block_end>();
96 scope.define_func::<__breakpoint_show_start>();
97 scope.define_func::<__breakpoint_show_end>();
98 scope.define_func::<__breakpoint_doc_start>();
99 scope.define_func::<__breakpoint_doc_end>();
100
101 scope.define_func::<__breakpoint_call_start_handle>();
102 scope.define_func::<__breakpoint_call_end_handle>();
103 scope.define_func::<__breakpoint_function_handle>();
104 scope.define_func::<__breakpoint_break_handle>();
105 scope.define_func::<__breakpoint_continue_handle>();
106 scope.define_func::<__breakpoint_return_handle>();
107 scope.define_func::<__breakpoint_block_start_handle>();
108 scope.define_func::<__breakpoint_block_end_handle>();
109 scope.define_func::<__breakpoint_show_start_handle>();
110 scope.define_func::<__breakpoint_show_end_handle>();
111 scope.define_func::<__breakpoint_doc_start_handle>();
112 scope.define_func::<__breakpoint_doc_end_handle>();
113
114 Arc::new(library)
115}
116
117#[derive(PartialEq, Eq, PartialOrd, Ord)]
118struct PackageSpecCmp<'a> {
119 pub namespace: &'a EcoString,
121 pub name: &'a EcoString,
123 pub version: &'a PackageVersion,
125}
126
127impl<'a> From<&'a PackageSpec> for PackageSpecCmp<'a> {
128 fn from(spec: &'a PackageSpec) -> Self {
129 Self {
130 namespace: &spec.namespace,
131 name: &spec.name,
132 version: &spec.version,
133 }
134 }
135}