rune/runtime/
debug.rs

1//! Debug information for units.
2
3use core::fmt;
4
5#[cfg(feature = "musli")]
6use musli::{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))]
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}
32
33impl DebugInfo {
34    /// Get debug instruction at the given instruction pointer.
35    pub fn instruction_at(&self, ip: usize) -> Option<&DebugInst> {
36        self.instructions.get(&ip)
37    }
38
39    /// Get the function corresponding to the given instruction pointer.
40    pub fn function_at(&self, ip: usize) -> Option<(Hash, &DebugSignature)> {
41        let hash = *self.functions_rev.get(&ip)?;
42        let signature = self.functions.get(&hash)?;
43        Some((hash, signature))
44    }
45
46    /// Access an identifier for the given hash - if it exists.
47    pub fn ident_for_hash(&self, hash: Hash) -> Option<&str> {
48        Some(self.hash_to_ident.get(&hash)?)
49    }
50}
51
52/// Debug information for every instruction.
53#[derive(Debug, TryClone)]
54#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
55#[cfg_attr(feature = "musli", derive(Decode, Encode))]
56#[non_exhaustive]
57pub struct DebugInst {
58    /// The file by id the instruction belongs to.
59    pub source_id: SourceId,
60    /// The span of the instruction.
61    pub span: Span,
62    /// The comment for the line.
63    pub comment: Option<Box<str>>,
64    /// Label associated with the location.
65    pub labels: Vec<DebugLabel>,
66}
67
68impl DebugInst {
69    /// Construct a new debug instruction.
70    pub fn new(
71        source_id: SourceId,
72        span: Span,
73        comment: Option<Box<str>>,
74        labels: Vec<DebugLabel>,
75    ) -> Self {
76        Self {
77            source_id,
78            span,
79            comment,
80            labels,
81        }
82    }
83}
84
85/// Debug information on function arguments.
86#[derive(Debug, TryClone)]
87#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
88#[cfg_attr(feature = "musli", derive(Decode, Encode))]
89#[non_exhaustive]
90pub enum DebugArgs {
91    /// An empty, with not arguments.
92    EmptyArgs,
93    /// A tuple, with the given number of arguments.
94    TupleArgs(usize),
95    /// A collection of named arguments.
96    Named(Box<[Box<str>]>),
97}
98
99/// A description of a function signature.
100#[derive(Debug, TryClone)]
101#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
102#[cfg_attr(feature = "musli", derive(Decode, Encode))]
103#[non_exhaustive]
104pub struct DebugSignature {
105    /// The path of the function.
106    pub path: ItemBuf,
107    /// The number of arguments expected in the function.
108    pub args: DebugArgs,
109}
110
111impl DebugSignature {
112    /// Construct a new function signature.
113    #[inline]
114    pub fn new(path: ItemBuf, args: DebugArgs) -> Self {
115        Self { path, args }
116    }
117}
118
119impl fmt::Display for DebugSignature {
120    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
121        match &self.args {
122            DebugArgs::EmptyArgs => {
123                write!(fmt, "{}", self.path)?;
124            }
125            DebugArgs::TupleArgs(args) if *args > 0 => {
126                write!(fmt, "{}(", self.path)?;
127
128                let mut it = 0..*args;
129                let last = it.next_back();
130
131                for arg in it {
132                    write!(fmt, "{arg}, ")?;
133                }
134
135                if let Some(arg) = last {
136                    write!(fmt, "{arg}")?;
137                }
138
139                write!(fmt, ")")?;
140            }
141            DebugArgs::Named(args) => {
142                write!(fmt, "{}(", self.path)?;
143
144                let mut it = args.iter();
145                let last = it.next_back();
146
147                for arg in it {
148                    write!(fmt, "{arg}, ")?;
149                }
150
151                if let Some(arg) = last {
152                    write!(fmt, "{arg}")?;
153                }
154
155                write!(fmt, ")")?;
156            }
157            _ => (),
158        }
159
160        Ok(())
161    }
162}