Skip to main content

rune/runtime/
debug.rs

1//! Debug information for units.
2
3use core::fmt;
4
5#[cfg(feature = "musli")]
6use musli_core::{Decode, Encode};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10use crate as rune;
11use crate::alloc::prelude::*;
12use crate::alloc::{Box, HashMap, Vec};
13use crate::ast::Span;
14use crate::runtime::DebugLabel;
15use crate::{Hash, ItemBuf, SourceId};
16
17/// Debug information about a unit.
18#[derive(Debug, TryClone, Default)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
21#[non_exhaustive]
22pub struct DebugInfo {
23    /// Debug information on each instruction.
24    pub instructions: HashMap<usize, DebugInst>,
25    /// Function signatures.
26    pub functions: HashMap<Hash, DebugSignature>,
27    /// Reverse lookup of a function.
28    pub functions_rev: HashMap<usize, Hash>,
29    /// Hash to identifier.
30    pub hash_to_ident: HashMap<Hash, Box<str>>,
31    /// Static slots, indexed by slot.
32    ///
33    /// This is empty if debug information has been disabled, in which case
34    /// diagnostics have to refer to a static by its slot index.
35    pub globals: Vec<DebugGlobal>,
36}
37
38impl DebugInfo {
39    /// Get debug instruction at the given instruction pointer.
40    pub fn instruction_at(&self, ip: usize) -> Option<&DebugInst> {
41        self.instructions.get(&ip)
42    }
43
44    /// Get the function corresponding to the given instruction pointer.
45    pub fn function_at(&self, ip: usize) -> Option<(Hash, &DebugSignature)> {
46        let hash = *self.functions_rev.get(&ip)?;
47        let signature = self.functions.get(&hash)?;
48        Some((hash, signature))
49    }
50
51    /// Access an identifier for the given hash - if it exists.
52    pub fn ident_for_hash(&self, hash: Hash) -> Option<&str> {
53        Some(self.hash_to_ident.get(&hash)?)
54    }
55
56    /// Access debug information for the given static slot - if it exists.
57    pub fn global(&self, slot: usize) -> Option<&DebugGlobal> {
58        self.globals.get(slot)
59    }
60}
61
62/// Debug information for a static slot.
63#[derive(Debug, TryClone)]
64#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
66#[non_exhaustive]
67pub struct DebugGlobal {
68    /// The path of the static item occupying the slot.
69    pub path: ItemBuf,
70}
71
72impl DebugGlobal {
73    /// Construct a new static slot description.
74    #[inline]
75    pub fn new(path: ItemBuf) -> Self {
76        Self { path }
77    }
78}
79
80impl fmt::Display for DebugGlobal {
81    #[inline]
82    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
83        write!(fmt, "{}", self.path)
84    }
85}
86
87/// Debug information for every instruction.
88#[derive(Debug, TryClone)]
89#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
90#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
91#[non_exhaustive]
92pub struct DebugInst {
93    /// The file by id the instruction belongs to.
94    pub source_id: SourceId,
95    /// The span of the instruction.
96    pub span: Span,
97    /// The comment for the line.
98    pub comment: Option<Box<str>>,
99    /// Label associated with the location.
100    pub labels: Vec<DebugLabel>,
101}
102
103impl DebugInst {
104    /// Construct a new debug instruction.
105    pub fn new(
106        source_id: SourceId,
107        span: Span,
108        comment: Option<Box<str>>,
109        labels: Vec<DebugLabel>,
110    ) -> Self {
111        Self {
112            source_id,
113            span,
114            comment,
115            labels,
116        }
117    }
118}
119
120/// Debug information on function arguments.
121#[derive(Debug, TryClone)]
122#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
123#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
124#[non_exhaustive]
125pub enum DebugArgs {
126    /// An empty, with not arguments.
127    EmptyArgs,
128    /// A tuple, with the given number of arguments.
129    TupleArgs(usize),
130    /// A collection of named arguments.
131    Named(Box<[Box<str>]>),
132}
133
134/// A description of a function signature.
135#[derive(Debug, TryClone)]
136#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
137#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
138#[non_exhaustive]
139pub struct DebugSignature {
140    /// The path of the function.
141    pub path: ItemBuf,
142    /// The number of arguments expected in the function.
143    pub args: DebugArgs,
144}
145
146impl DebugSignature {
147    /// Construct a new function signature.
148    #[inline]
149    pub fn new(path: ItemBuf, args: DebugArgs) -> Self {
150        Self { path, args }
151    }
152}
153
154impl fmt::Display for DebugSignature {
155    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
156        match &self.args {
157            DebugArgs::EmptyArgs => {
158                write!(fmt, "{}", self.path)?;
159            }
160            DebugArgs::TupleArgs(args) if *args > 0 => {
161                write!(fmt, "{}(", self.path)?;
162
163                let mut it = 0..*args;
164                let last = it.next_back();
165
166                for arg in it {
167                    write!(fmt, "{arg}, ")?;
168                }
169
170                if let Some(arg) = last {
171                    write!(fmt, "{arg}")?;
172                }
173
174                write!(fmt, ")")?;
175            }
176            DebugArgs::Named(args) => {
177                write!(fmt, "{}(", self.path)?;
178
179                let mut it = args.iter();
180                let last = it.next_back();
181
182                for arg in it {
183                    write!(fmt, "{arg}, ")?;
184                }
185
186                if let Some(arg) = last {
187                    write!(fmt, "{arg}")?;
188                }
189
190                write!(fmt, ")")?;
191            }
192            _ => (),
193        }
194
195        Ok(())
196    }
197}