Skip to main content

miden_debug_types/
source_manager.rs

1use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc};
2use core::{error::Error, fmt::Debug};
3
4use miden_utils_indexing::IndexVec;
5#[cfg(feature = "arbitrary")]
6use proptest::prelude::*;
7
8use super::*;
9
10// SOURCE ID
11// ================================================================================================
12
13/// A [SourceId] represents the index/identifier associated with a unique source file in a
14/// [SourceManager] implementation.
15#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
17#[cfg_attr(feature = "serde", serde(transparent))]
18#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
19pub struct SourceId(u32);
20
21impl From<u32> for SourceId {
22    fn from(value: u32) -> Self {
23        SourceId::new_unchecked(value)
24    }
25}
26
27impl From<SourceId> for u32 {
28    fn from(value: SourceId) -> Self {
29        value.to_u32()
30    }
31}
32
33impl miden_utils_indexing::Idx for SourceId {}
34
35impl Default for SourceId {
36    fn default() -> Self {
37        Self::UNKNOWN
38    }
39}
40
41impl SourceId {
42    pub const UNKNOWN: Self = Self(u32::MAX);
43
44    /// Create a new [SourceId] from a `u32` value, but assert if the value is reserved
45    pub fn new(id: u32) -> Self {
46        assert_ne!(id, u32::MAX, "u32::MAX is a reserved value for SourceId::default()/UNKNOWN");
47
48        Self(id)
49    }
50
51    /// Create a new [SourceId] from a raw `u32` value
52    #[inline(always)]
53    pub const fn new_unchecked(id: u32) -> Self {
54        Self(id)
55    }
56
57    #[inline(always)]
58    pub const fn to_usize(self) -> usize {
59        self.0 as usize
60    }
61
62    #[inline(always)]
63    pub const fn to_u32(self) -> u32 {
64        self.0
65    }
66
67    pub const fn is_unknown(&self) -> bool {
68        self.0 == u32::MAX
69    }
70}
71
72impl TryFrom<usize> for SourceId {
73    type Error = ();
74
75    #[inline]
76    fn try_from(id: usize) -> Result<Self, Self::Error> {
77        match u32::try_from(id) {
78            Ok(n) if n < u32::MAX => Ok(Self(n)),
79            _ => Err(()),
80        }
81    }
82}
83
84#[cfg(feature = "arbitrary")]
85impl Arbitrary for SourceId {
86    type Parameters = ();
87    type Strategy = BoxedStrategy<Self>;
88
89    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
90        any::<u32>().prop_map(Self::from).boxed()
91    }
92}
93
94// SOURCE MANAGER
95// ================================================================================================
96
97/// The set of errors which may be raised by a [SourceManager]
98#[derive(Debug, thiserror::Error)]
99pub enum SourceManagerError {
100    /// A [SourceId] was provided to a [SourceManager] which was allocated by a different
101    /// [SourceManager]
102    #[error("attempted to use an invalid source id")]
103    InvalidSourceId,
104    /// An attempt was made to read content using invalid byte indices
105    #[error("attempted to read content out of bounds")]
106    InvalidBounds,
107    #[error(transparent)]
108    InvalidContentUpdate(#[from] SourceContentUpdateError),
109    /// Custom error variant for implementors of the trait.
110    #[error("{error_msg}")]
111    Custom {
112        error_msg: Box<str>,
113        // thiserror will return this when calling Error::source on SourceManagerError.
114        source: Option<Box<dyn Error + Send + Sync + 'static>>,
115    },
116}
117
118impl SourceManagerError {
119    pub fn custom(message: String) -> Self {
120        Self::Custom { error_msg: message.into(), source: None }
121    }
122
123    pub fn custom_with_source(message: String, source: impl Error + Send + Sync + 'static) -> Self {
124        Self::Custom {
125            error_msg: message.into(),
126            source: Some(Box::new(source)),
127        }
128    }
129}
130
131pub trait SourceManager: Debug {
132    /// Returns true if `file` is managed by this source manager
133    fn is_manager_of(&self, file: &SourceFile) -> bool {
134        match self.get(file.id()) {
135            Ok(found) => core::ptr::addr_eq(Arc::as_ptr(&found), file),
136            Err(_) => false,
137        }
138    }
139    /// Copies `file` into this source manager (if not already managed by this manager).
140    ///
141    /// The returned source file is guaranteed to be owned by this manager.
142    fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
143        if let Ok(found) = self.get(file.id())
144            && core::ptr::addr_eq(Arc::as_ptr(&found), file)
145        {
146            return found;
147        }
148        self.load_from_raw_parts(file.uri().clone(), file.content().clone())
149    }
150    /// Load the given `content` into this [SourceManager] with `name`
151    fn load(&self, lang: SourceLanguage, name: Uri, content: String) -> Arc<SourceFile> {
152        let content = SourceContent::new(lang, name.clone(), content);
153        self.load_from_raw_parts(name, content)
154    }
155    /// Load the given `content` into this [SourceManager] with a name generated from the hash of
156    /// its contents
157    fn load_anonymous(&self, lang: SourceLanguage, content: String) -> Arc<SourceFile> {
158        use alloc::format;
159
160        use miden_crypto::hash::sha2::Sha256;
161        let digest = Sha256::hash(content.as_bytes());
162        let name = Uri::new(format!("memory://{}", String::from(digest)));
163        let content = SourceContent::new(lang, name.clone(), content);
164        self.load_from_raw_parts(name, content)
165    }
166    /// Load content into this [SourceManager] from raw [SourceFile] components
167    fn load_from_raw_parts(&self, name: Uri, content: SourceContent) -> Arc<SourceFile>;
168    /// Update the source file corresponding to `id` after being notified of a change event.
169    ///
170    /// The `version` indicates the new version of the document
171    fn update(
172        &self,
173        id: SourceId,
174        text: String,
175        range: Option<Selection>,
176        version: i32,
177    ) -> Result<(), SourceManagerError>;
178    /// Get the [SourceFile] corresponding to `id`
179    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError>;
180    /// Get the most recent [SourceFile] whose URI is `uri`
181    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
182        self.find(uri).and_then(|id| self.get(id).ok())
183    }
184    /// Search for a source file whose URI is `uri`, and return its [SourceId] if found.
185    fn find(&self, uri: &Uri) -> Option<SourceId>;
186    /// Convert a [FileLineCol] to an equivalent [SourceSpan], if the referenced file is available
187    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan>;
188    /// Convert a [SourceSpan] to an equivalent [FileLineCol], if the span is valid
189    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError>;
190    /// Convert a [Location] to an equivalent [SourceSpan], if the referenced file is available
191    fn location_to_span(&self, loc: Location) -> Option<SourceSpan>;
192    /// Convert a [SourceSpan] to an equivalent [Location], if the span is valid
193    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError>;
194    /// Get the source associated with `id` as a string slice
195    fn source(&self, id: SourceId) -> Result<&str, SourceManagerError>;
196    /// Get the source corresponding to `span` as a string slice
197    fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError>;
198}
199
200impl<T: ?Sized + SourceManager> SourceManager for Arc<T> {
201    #[inline(always)]
202    fn is_manager_of(&self, file: &SourceFile) -> bool {
203        (**self).is_manager_of(file)
204    }
205    #[inline(always)]
206    fn copy_into(&self, file: &SourceFile) -> Arc<SourceFile> {
207        (**self).copy_into(file)
208    }
209    #[inline(always)]
210    fn load(&self, lang: SourceLanguage, uri: Uri, content: String) -> Arc<SourceFile> {
211        (**self).load(lang, uri, content)
212    }
213    #[inline(always)]
214    fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
215        (**self).load_from_raw_parts(uri, content)
216    }
217    #[inline(always)]
218    fn update(
219        &self,
220        id: SourceId,
221        text: String,
222        range: Option<Selection>,
223        version: i32,
224    ) -> Result<(), SourceManagerError> {
225        (**self).update(id, text, range, version)
226    }
227    #[inline(always)]
228    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
229        (**self).get(id)
230    }
231    #[inline(always)]
232    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
233        (**self).get_by_uri(uri)
234    }
235    #[inline(always)]
236    fn find(&self, uri: &Uri) -> Option<SourceId> {
237        (**self).find(uri)
238    }
239    #[inline(always)]
240    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
241        (**self).file_line_col_to_span(loc)
242    }
243    #[inline(always)]
244    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
245        (**self).file_line_col(span)
246    }
247    #[inline(always)]
248    fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
249        (**self).location_to_span(loc)
250    }
251    #[inline(always)]
252    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
253        (**self).location(span)
254    }
255    #[inline(always)]
256    fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
257        (**self).source(id)
258    }
259    #[inline(always)]
260    fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
261        (**self).source_slice(span)
262    }
263}
264
265#[cfg(feature = "std")]
266pub trait SourceManagerExt: SourceManager {
267    /// Load the content of `path` into this [SourceManager]
268    fn load_file(&self, path: &std::path::Path) -> Result<Arc<SourceFile>, SourceManagerError> {
269        let uri = Uri::from(path);
270        let content = std::fs::read_to_string(path).map_err(|source| {
271            SourceManagerError::custom_with_source(
272                alloc::format!("failed to load file at `{}`", path.display()),
273                source,
274            )
275        })?;
276
277        // Return the already-allocated file if it has already been loaded and with change since
278        if let Some(existing) = self.get_by_uri(&uri)
279            && existing.as_str() == content.as_str()
280        {
281            return Ok(existing);
282        }
283
284        let lang = match path.extension().and_then(|ext| ext.to_str()) {
285            Some("masm") => "masm",
286            Some("rs") => "rust",
287            Some(ext) => ext,
288            None => "unknown",
289        };
290
291        let content = std::fs::read_to_string(path)
292            .map(|s| SourceContent::new(lang, uri.clone(), s))
293            .map_err(|source| {
294                SourceManagerError::custom_with_source(
295                    alloc::format!("failed to load file at `{}`", path.display()),
296                    source,
297                )
298            })?;
299
300        Ok(self.load_from_raw_parts(uri, content))
301    }
302}
303
304#[cfg(feature = "std")]
305impl<T: ?Sized + SourceManager> SourceManagerExt for T {}
306
307/// [SourceManagerSync] is a marker trait for [SourceManager] implementations that are also Send +
308/// Sync, and is automatically implemented for any [SourceManager] that meets those requirements.
309///
310/// [SourceManager] is a supertrait of [SourceManagerSync], so you may use instances of the
311/// [SourceManagerSync] where the [SourceManager] is required, either implicitly or via explicit
312/// downcasting, e.g. `Arc<dyn SourceManagerSync> as Arc<dyn SourceManager>`.
313pub trait SourceManagerSync: SourceManager + Send + Sync {}
314
315impl<T: ?Sized + SourceManager + Send + Sync> SourceManagerSync for T {}
316
317// DEFAULT SOURCE MANAGER
318// ================================================================================================
319
320use miden_utils_sync::RwLock;
321
322#[derive(Debug, Default)]
323pub struct DefaultSourceManager(RwLock<DefaultSourceManagerImpl>);
324
325impl Default for DefaultSourceManagerImpl {
326    fn default() -> Self {
327        Self::new()
328    }
329}
330impl Clone for DefaultSourceManager {
331    fn clone(&self) -> Self {
332        let manager = self.0.read();
333        Self(RwLock::new(manager.clone()))
334    }
335}
336
337impl Clone for DefaultSourceManagerImpl {
338    fn clone(&self) -> Self {
339        Self {
340            files: self.files.clone(),
341            uris: self.uris.clone(),
342        }
343    }
344}
345
346#[derive(Debug)]
347struct DefaultSourceManagerImpl {
348    files: IndexVec<SourceId, Arc<SourceFile>>,
349    uris: BTreeMap<Uri, SourceId>,
350}
351
352impl DefaultSourceManagerImpl {
353    fn new() -> Self {
354        Self {
355            files: IndexVec::new(),
356            uris: BTreeMap::new(),
357        }
358    }
359
360    fn insert(&mut self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
361        // If we have previously inserted the same content with `name`, return the previously
362        // inserted source id
363        if let Some(file) = self.uris.get(&uri).copied().and_then(|id| {
364            let file = &self.files[id];
365            if file.as_str() == content.as_str() {
366                Some(Arc::clone(file))
367            } else {
368                None
369            }
370        }) {
371            return file;
372        }
373        let id = SourceId::try_from(self.files.len())
374            .expect("system limit: source manager has exhausted its supply of source ids");
375        let file = Arc::new(SourceFile::from_raw_parts(id, content));
376        let file_clone = Arc::clone(&file);
377        self.files
378            .push(file_clone)
379            .expect("system limit: source manager has exhausted its supply of source ids");
380        self.uris.insert(uri, id);
381        file
382    }
383
384    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
385        self.files.get(id).cloned().ok_or(SourceManagerError::InvalidSourceId)
386    }
387
388    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
389        self.find(uri).and_then(|id| self.get(id).ok())
390    }
391
392    fn find(&self, uri: &Uri) -> Option<SourceId> {
393        self.uris.get(uri).copied()
394    }
395
396    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
397        let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id))?;
398        file.line_column_to_span(loc.line, loc.column)
399    }
400
401    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
402        self.files
403            .get(span.source_id())
404            .ok_or(SourceManagerError::InvalidSourceId)
405            .map(|file| file.location(span))
406    }
407
408    fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
409        let file = self.uris.get(&loc.uri).copied().and_then(|id| self.files.get(id))?;
410
411        let max_len = ByteIndex::from(file.as_str().len() as u32);
412        if loc.start >= max_len || loc.end > max_len {
413            return None;
414        }
415
416        Some(SourceSpan::new(file.id(), loc.start..loc.end))
417    }
418
419    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
420        self.files
421            .get(span.source_id())
422            .ok_or(SourceManagerError::InvalidSourceId)
423            .map(|file| Location::new(file.uri().clone(), span.start(), span.end()))
424    }
425}
426
427impl SourceManager for DefaultSourceManager {
428    fn load_from_raw_parts(&self, uri: Uri, content: SourceContent) -> Arc<SourceFile> {
429        let mut manager = self.0.write();
430        manager.insert(uri, content)
431    }
432
433    fn update(
434        &self,
435        id: SourceId,
436        text: String,
437        range: Option<Selection>,
438        version: i32,
439    ) -> Result<(), SourceManagerError> {
440        let mut manager = self.0.write();
441        let source_file = &mut manager.files[id];
442        let source_file_cloned = Arc::make_mut(source_file);
443        source_file_cloned
444            .content_mut()
445            .update(text, range, version)
446            .map_err(SourceManagerError::InvalidContentUpdate)
447    }
448
449    fn get(&self, id: SourceId) -> Result<Arc<SourceFile>, SourceManagerError> {
450        let manager = self.0.read();
451        manager.get(id)
452    }
453
454    fn get_by_uri(&self, uri: &Uri) -> Option<Arc<SourceFile>> {
455        let manager = self.0.read();
456        manager.get_by_uri(uri)
457    }
458
459    fn find(&self, uri: &Uri) -> Option<SourceId> {
460        let manager = self.0.read();
461        manager.find(uri)
462    }
463
464    fn file_line_col_to_span(&self, loc: FileLineCol) -> Option<SourceSpan> {
465        let manager = self.0.read();
466        manager.file_line_col_to_span(loc)
467    }
468
469    fn file_line_col(&self, span: SourceSpan) -> Result<FileLineCol, SourceManagerError> {
470        let manager = self.0.read();
471        manager.file_line_col(span)
472    }
473
474    fn location_to_span(&self, loc: Location) -> Option<SourceSpan> {
475        let manager = self.0.read();
476        manager.location_to_span(loc)
477    }
478
479    fn location(&self, span: SourceSpan) -> Result<Location, SourceManagerError> {
480        let manager = self.0.read();
481        manager.location(span)
482    }
483
484    fn source(&self, id: SourceId) -> Result<&str, SourceManagerError> {
485        let manager = self.0.read();
486        let ptr = manager
487            .files
488            .get(id)
489            .ok_or(SourceManagerError::InvalidSourceId)
490            .map(|file| file.as_str() as *const str)?;
491        drop(manager);
492        // SAFETY: Because the lifetime of the returned reference is bound to the manager, and
493        // because we can only ever add files, not modify/remove them, this is safe. Exclusive
494        // access to the manager does _not_ mean exclusive access to the contents of previously
495        // added source files
496        Ok(unsafe { &*ptr })
497    }
498
499    fn source_slice(&self, span: SourceSpan) -> Result<&str, SourceManagerError> {
500        self.source(span.source_id())?
501            .get(span.into_slice_index())
502            .ok_or(SourceManagerError::InvalidBounds)
503    }
504}
505
506#[cfg(test)]
507mod error_assertions {
508    use super::*;
509
510    /// Asserts at compile time that the passed error has Send + Sync + 'static bounds.
511    fn _assert_error_is_send_sync_static<E: Error + Send + Sync + 'static>(_: E) {}
512
513    fn _assert_source_manager_error_bounds(err: SourceManagerError) {
514        _assert_error_is_send_sync_static(err);
515    }
516}