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