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