yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Lujin WangFix missing debug info for the included slang file (#7281)0c4c63b4a

master
27.5 KiB693 linesraw
1// slang-source-loc.h
2#ifndef SLANG_SOURCE_LOC_H_INCLUDED
3#define SLANG_SOURCE_LOC_H_INCLUDED
4
5#include "../core/slang-basic.h"
6#include "../core/slang-castable.h"
7#include "../core/slang-crypto.h"
8#include "../core/slang-memory-arena.h"
9#include "../core/slang-string-slice-pool.h"
10#include "slang-com-ptr.h"
11#include "slang-source-map.h"
12#include "slang.h"
13
14namespace Slang
15{
16
17/** Overview:
18
19There needs to be a mechanism where we can easily and quickly track a specific locations in any
20source file used during a compilation. This is important because that original location is
21meaningful to the user as it relates to their original source. Thus SourceLoc are used so we can
22display meaningful and accurate errors/warnings as well as being able to always map generated code
23locations back to their origins.
24
25A 'SourceLoc' along with associated structures (SourceView, SourceFile, SourceMangager) this can
26pinpoint the location down to the byte across the compilation. This could be achieved by storing for
27every token and instruction the file, line and column number came from. The SourceLoc is used in
28lots of places - every AST node, every Token from the lexer, every IRInst - so we really want to
29make it small. So for this reason we actually encode SourceLoc as a single integer and then use the
30associated structures when needed to determine what the location actually refers to - the source
31file, line and column number, or in effect the byte in the original file.
32
33Unfortunately there is extra complications. When a source is parsed it's interpretation (in terms of
34how a piece of source maps to an 'original' file etc) can be overridden - for example by using #line
35directives. Moreover a single source file can be parsed multiple times. When it's parsed multiple
36times the interpretation of the mapping (#line directives for example) can change. This is the
37purpose of the SourceView - it holds the interpretation of a source file for a specific Lex/Parse.
38
39Another complication is that not all 'source' comes from SourceFiles, a macro expansion, may
40generate new 'source' we need to handle this, but also be able to have a SourceLoc map to the
41expansion unambiguously. This is handled by creating a SourceFile and SourceView that holds only the
42macro generated specific information.
43
44SourceFile - Is the immutable text contents of a file (or perhaps some generated source - say from
45doing a macro substitution) SourceView - Tracks a single parse of a SourceFile. Each SourceView
46defines a range of source locations used. If a SourceFile is parsed twice, two SourceViews are
47created, with unique SourceRanges. This is so that it is possible to tell which specific parse a
48SourceLoc is from - and so know the right interpretation for that lex/parse.
49*/
50
51struct PathInfo
52{
53    typedef PathInfo ThisType;
54
55    /// To be more rigorous about where a path comes from, the type identifies what a paths origin
56    /// is
57    enum class Type : uint8_t
58    {
59        Unknown,    ///< The path is not known
60        Normal,     ///< Normal has both path and uniqueIdentity
61        FoundPath,  ///< Just has a found path (uniqueIdentity is unknown, or even 'unknowable')
62        FromString, ///< Created from a string (so found path might not be defined and should not be
63                    ///< taken as to map to a loaded file)
64        TokenPaste, ///< No paths, just created to do a macro expansion
65        TypeParse,  ///< No path, just created to do a type parse
66        CommandLine, ///< A macro constructed from the command line
67    };
68
69    /// True if has a canonical path
70    SLANG_FORCE_INLINE bool hasUniqueIdentity() const
71    {
72        return type == Type::Normal && uniqueIdentity.getLength() > 0;
73    }
74    /// True if has a regular found path
75    SLANG_FORCE_INLINE bool hasFoundPath() const
76    {
77        return (type == Type::Normal || type == Type::FoundPath || type == Type::FromString) &&
78               foundPath.getLength() > 0;
79    }
80    /// True if has a found path that has originated from a file (as opposed to string or some other
81    /// origin)
82    SLANG_FORCE_INLINE bool hasFileFoundPath() const
83    {
84        return (type == Type::Normal || type == Type::FoundPath) && foundPath.getLength() > 0;
85    }
86    /// Get the 'name'/path of the item. Will return an empty string if not applicable or not set.
87    String getName() const;
88
89    bool operator==(const ThisType& rhs) const;
90    bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
91
92    /// Returns the 'most unique' identity for the path. If has a 'uniqueIdentity' returns that,
93    /// else the foundPath, else "".
94    const String getMostUniqueIdentity() const;
95
96    /// Append to out, how to display the path
97    void appendDisplayName(StringBuilder& out) const;
98
99    // So simplify construction. In normal usage it's safer to use make methods over constructing
100    // directly.
101    static PathInfo makeUnknown() { return PathInfo{Type::Unknown, String(), String()}; }
102    static PathInfo makeTokenPaste() { return PathInfo{Type::TokenPaste, "token paste", String()}; }
103    static PathInfo makeNormal(const String& foundPathIn, const String& uniqueIdentity)
104    {
105        SLANG_ASSERT(uniqueIdentity.getLength() > 0 && foundPathIn.getLength() > 0);
106        return PathInfo{Type::Normal, foundPathIn, uniqueIdentity};
107    }
108    static PathInfo makePath(const String& pathIn)
109    {
110        SLANG_ASSERT(pathIn.getLength() > 0);
111        return PathInfo{Type::FoundPath, pathIn, String()};
112    }
113    static PathInfo makeTypeParse() { return PathInfo{Type::TypeParse, "type string", String()}; }
114    static PathInfo makeCommandLine()
115    {
116        return PathInfo{Type::CommandLine, "command line", String()};
117    }
118    static PathInfo makeFromString(const String& userPath)
119    {
120        return PathInfo{Type::FromString, userPath, String()};
121    }
122
123    Type type;             ///< The type of path
124    String foundPath;      ///< The path where the file was found (might contain relative elements)
125    String uniqueIdentity; ///< The unique identity of the file on the path found
126};
127
128class SourceLoc
129{
130public:
131    typedef SourceLoc ThisType;
132    typedef uint32_t RawValue;
133
134private:
135    RawValue raw;
136
137public:
138    SourceLoc()
139        : raw(0)
140    {
141    }
142
143    SourceLoc(SourceLoc const& loc)
144        : raw(loc.raw)
145    {
146    }
147
148    SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return raw == rhs.raw; }
149    SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(raw == rhs.raw); }
150    SLANG_FORCE_INLINE bool operator<(const ThisType& rhs) const { return raw < rhs.raw; }
151    SLANG_FORCE_INLINE bool operator>(const ThisType& rhs) const { return raw > rhs.raw; }
152    SLANG_FORCE_INLINE bool operator<=(const ThisType& rhs) const { return raw <= rhs.raw; }
153    SLANG_FORCE_INLINE bool operator>=(const ThisType& rhs) const { return raw >= rhs.raw; }
154
155    RawValue getRaw() const { return raw; }
156    void setRaw(RawValue value) { raw = value; }
157
158    static SourceLoc fromRaw(RawValue value)
159    {
160        SourceLoc result;
161        result.setRaw(value);
162        return result;
163    }
164
165    bool isValid() const { return raw != 0; }
166    SourceLoc& operator=(const ThisType& rhs) = default;
167};
168
169inline SourceLoc operator+(SourceLoc loc, Int offset)
170{
171    return SourceLoc::fromRaw(SourceLoc::RawValue(Int(loc.getRaw()) + offset));
172}
173
174// A range of locations in the input source
175struct SourceRange
176{
177    /// True if the loc is in the range. Range is inclusive on begin to end.
178    bool contains(SourceLoc loc) const
179    {
180        const auto rawLoc = loc.getRaw();
181        return rawLoc >= begin.getRaw() && rawLoc <= end.getRaw();
182    }
183    /// Get the total size
184    SourceLoc::RawValue getSize() const { return end.getRaw() - begin.getRaw(); }
185
186    /// Get the offset of a loc in this range
187    int getOffset(SourceLoc loc) const
188    {
189        SLANG_ASSERT(contains(loc));
190        return int(loc.getRaw() - begin.getRaw());
191    }
192
193    /// Convert an offset to a loc
194    SourceLoc getSourceLocFromOffset(uint32_t offset) const
195    {
196        SLANG_ASSERT(offset <= getSize());
197        return begin + offset;
198    }
199
200    SourceRange() {}
201
202    SourceRange(SourceLoc loc)
203        : begin(loc), end(loc)
204    {
205    }
206
207    SourceRange(SourceLoc begin, SourceLoc end)
208        : begin(begin), end(end)
209    {
210    }
211
212    SourceLoc begin;
213    SourceLoc end;
214};
215
216/// Source maps associated with files are could be of different uses. We use the SourceMapKind
217/// to indicate the usage.
218///
219/// If the source map is obfuscated reasonable/desirable to ignore them on emit (if we didn't we
220/// leak information, and we don't emit into the locations in the obfuscated intermediate "file").
221enum class SourceMapKind
222{
223    Normal,     ///< A regular source map
224    Obfuscated, ///< Obfuscated source map
225};
226
227// Pre-declare
228struct SourceManager;
229
230// A logical or physical storage object for a range of input code
231// that has logically contiguous source locations.
232class SourceFile
233{
234public:
235    struct OffsetRange
236    {
237        /// We need a value to indicate an invalid range. We can't use 0 as that is valid for an
238        /// offset range We can't use a negative number, and don't want to make signed so we get the
239        /// full 32-bits. So we just use the max value as invalid
240        static const uint32_t kInvalid = 0xffffffff;
241
242        /// True if the range is valid
243        SLANG_FORCE_INLINE bool isValid() const { return end >= start && start != kInvalid; }
244        /// True if offset is within range (inclusively)
245        SLANG_FORCE_INLINE bool containsInclusive(uint32_t offset) const
246        {
247            return offset >= start && offset <= end;
248        }
249
250        /// Get the count
251        SLANG_FORCE_INLINE uint32_t getCount() const { return end - start; }
252
253        /// Return an invalid range.
254        static OffsetRange makeInvalid() { return OffsetRange{kInvalid, kInvalid}; }
255
256        uint32_t start;
257        uint32_t end;
258    };
259
260    /// Returns the line break offsets (in bytes from start of content)
261    /// Note that this is lazily evaluated - the line breaks are only calculated on the first
262    /// request
263    const List<uint32_t>& getLineBreakOffsets();
264
265    /// Returns true if the offset is on the specified line
266    /// NOTE! If offsets are not fully setup (because we don't have source), will only be correct
267    /// for lines that have offsets
268    bool isOffsetOnLine(uint32_t offset, Index lineIndex);
269
270    /// Get the line containing the offset. Requires that content is available, else will return an
271    /// empty slice.
272    UnownedStringSlice getLineContainingOffset(uint32_t offset);
273
274    /// Get the line at the specified line index. Requires that content is available, else will
275    /// return an empty slice.
276    UnownedStringSlice getLineAtIndex(Index lineIndex);
277
278    /// Get the offset range at the specified line index. Works without content.
279    OffsetRange getOffsetRangeAtLineIndex(Index lineIndex);
280
281    /// Set the line break offsets
282    void setLineBreakOffsets(const uint32_t* offsets, UInt numOffsets);
283
284    /// Calculate the line based on the offset
285    int calcLineIndexFromOffset(int offset);
286
287    /// Calculate the offset (in bytes) for a line
288    int calcColumnOffset(int line, int offset);
289
290    /// Given a line and offset (in bytes for the whole file), return the column index, taking into
291    /// account tabs and utf8 encoding. Passing tabSize uses the default tab size (currently tab set
292    /// to 1)
293    int calcColumnIndex(int line, int offset, int tabSize = -1);
294
295    /// Get the content holding blob
296    ISlangBlob* getContentBlob() const { return m_contentBlob; }
297
298    /// True if has full set content
299    bool hasContent() const { return m_contentBlob != nullptr; }
300
301    /// Get the content size
302    size_t getContentSize() const { return m_contentSize; }
303
304    /// Get the content
305    const UnownedStringSlice& getContent() const { return m_content; }
306
307    /// Get path info
308    const PathInfo& getPathInfo() const { return m_pathInfo; }
309
310    /// Set the content as a blob
311    void setContents(ISlangBlob* blob);
312    /// Set the content as a string
313    void setContents(const String& content);
314
315    /// Calculate a display path -> can canonicalize if necessary
316    String calcVerbosePath() const;
317
318    /// Get the source manager this was created on
319    SourceManager* getSourceManager() const { return m_sourceManager; }
320
321    /// Get the source map associated with this file. If it's set when doing
322    /// lookup for source locations, the source map will be used
323    IBoxValue<SourceMap>* getSourceMap() const { return m_sourceMap; }
324    /// Get the source map kind
325    SourceMapKind getSourceMapKind() const { return m_sourceMapKind; }
326
327    /// Set a source map
328    void setSourceMap(IBoxValue<SourceMap>* sourceMap, SourceMapKind sourceMapKind)
329    {
330        m_sourceMap = sourceMap;
331        m_sourceMapKind = sourceMapKind;
332    }
333
334    /// Set the source file as an included file
335    void setIncludedFile() { m_included = true; }
336
337    /// Check if the source file is an included file
338    bool isIncludedFile() { return m_included; }
339
340    /// Ctor
341    SourceFile(SourceManager* sourceManager, const PathInfo& pathInfo, size_t contentSize);
342    /// Dtor
343    ~SourceFile();
344
345    SHA1::Digest getDigest();
346
347protected:
348    SourceManager* m_sourceManager; ///< The source manager this belongs to
349    PathInfo
350        m_pathInfo; ///< The path The logical file path to report for locations inside this span.
351
352    ComPtr<ISlangBlob> m_contentBlob; ///< A blob that owns the storage for the file contents. If
353                                      ///< nullptr, there is no contents
354    UnownedStringSlice m_content;     ///< The actual contents of the file.
355    size_t m_contentSize;             ///< The size of the actual contents
356
357    SHA1::Digest m_digest;
358
359    // In order to speed up lookup of line number information,
360    // we will cache the starting offset of each line break in
361    // the input file:
362    List<uint32_t> m_lineBreakOffsets;
363
364    // If set then the locations in this file are really from locations from elsewhere,
365    // where the SourceMap specifies that mapping
366    ComPtr<IBoxValue<SourceMap>> m_sourceMap;
367    // What kind of source map it is (if  there is one)
368    SourceMapKind m_sourceMapKind = SourceMapKind::Normal;
369
370    // Indicate if the source file is an included file
371    bool m_included = false;
372};
373
374enum class SourceLocType
375{
376    Nominal, ///< The normal interpretation which takes into account #line directives and source
377             ///< maps
378    Actual,  ///< Ignores #line directives/source maps - and is the location as seen in the actual
379             ///< file
380    Emit,    ///< Behaves the same as `Nominal` but ignores source maps. Used for Emit source
381             ///< locations.
382};
383
384// A source location in a format a human might like to see
385struct HumaneSourceLoc
386{
387    PathInfo pathInfo = PathInfo::makeUnknown();
388    Int line = 0;
389    Int column = 0;
390};
391
392// Same as HumaneSourceLoc but stores the path only as a handle.
393struct HandleSourceLoc
394{
395    StringSlicePool::Handle pathHandle = StringSlicePool::Handle(0);
396    Int line = 0;
397    Int column = 0;
398};
399
400/* A SourceView maps to a single span of SourceLoc range and is equivalent to a single include or
401more precisely use of a source file. It is distinct from a SourceFile - because a SourceFile may be
402included multiple times, with different interpretations (depending on #defines for example).
403*/
404class SourceView
405{
406public:
407    // Each entry represents some contiguous span of locations that
408    // all map to the same logical file.
409    struct Entry
410    {
411        /// True if this resets the line numbering. It is distinct from a m_lineAdjust being 0,
412        /// because it also means the path returns to the default.
413        bool isDefault() const { return m_pathHandle == StringSlicePool::Handle(0); }
414
415        SourceLoc m_startLoc;                 ///< Where does this entry begin?
416        StringSlicePool::Handle m_pathHandle; ///< What is the presumed path for this entry. If 0 it
417                                              ///< means there is no path.
418        int32_t m_lineAdjust; ///< Adjustment to apply to source line numbers when printing presumed
419                              ///< locations. Relative to the line number in the underlying file.
420    };
421
422    // Represents a segment of a source.
423    // All SourceLoc in segment are linearly mapped relative to absoluteBegin
424    struct AbsoluteSegment
425    {
426        // SourceLoc in the file range.
427        SourceLoc begin = {};
428        // Location in the absolute mapping of locations ordered by includes.
429        SourceLoc::RawValue absoluteBegin = {};
430    };
431
432    // Set the base of the absolute location mapping for this SourceView.
433    void setAbsoluteLocationBase(SourceLoc::RawValue absLoc) { m_absoluteLocationBase = absLoc; }
434
435    AbsoluteSegment getLastSegment() const
436    {
437        AbsoluteSegment res;
438        if (m_absSegments.getCount())
439        {
440            res = m_absSegments.getLast();
441        }
442        else
443        {
444            res.begin = m_range.begin;
445            res.absoluteBegin = m_absoluteLocationBase;
446        }
447        return res;
448    }
449
450    // Add a segment of absolute mapping after the previous ones.
451    void addAbsoluteSegment(SourceLoc begin, SourceLoc::RawValue absoluteBegin)
452    {
453        SLANG_ASSERT(m_range.contains(begin));
454        SLANG_ASSERT(getLastSegment().begin < begin);
455        AbsoluteSegment seg;
456        seg.begin = begin;
457        seg.absoluteBegin = absoluteBegin;
458        m_absSegments.add(seg);
459    }
460
461    // Maps a SourceLoc inside this SourceView to a unique absolute location ordered by includes.
462    SourceLoc::RawValue getAbsoluteLocation(SourceLoc loc) const;
463
464    /// Given a sourceLoc finds the entry associated with it. If returns -1 then no entry is
465    /// associated with this location, and therefore the location should be interpreted as an offset
466    /// into the underlying sourceFile.
467    int findEntryIndex(SourceLoc sourceLoc) const;
468
469    /// Add a line directive for this view. The directiveLoc must of course be in this SourceView
470    /// The path handle, must have been constructed on the SourceManager associated with the view
471    /// NOTE! Directives are assumed to be added IN ORDER during parsing such that every
472    /// directiveLoc > previous
473    void addLineDirective(SourceLoc directiveLoc, StringSlicePool::Handle pathHandle, int line);
474    void addLineDirective(SourceLoc directiveLoc, const String& path, int line);
475
476    /// Removes any corrections on line numbers and reverts to the source files path
477    void addDefaultLineDirective(SourceLoc directiveLoc);
478
479    /// Get the range that this view applies to
480    const SourceRange& getRange() const { return m_range; }
481    /// Get the entries
482    const List<Entry>& getEntries() const { return m_entries; }
483    /// Set the entries list
484    void setEntries(const Entry* entries, UInt numEntries)
485    {
486        m_entries.clear();
487        m_entries.addRange(entries, numEntries);
488    }
489
490    /// Get the source file holds the contents this view
491    SourceFile* getSourceFile() const { return m_sourceFile; }
492    /// Get the source manager
493    SourceManager* getSourceManager() const { return m_sourceFile->getSourceManager(); }
494
495    /// Get the associated 'content' (the source text)
496    const UnownedStringSlice& getContent() const { return m_sourceFile->getContent(); }
497
498    /// Get the size of the content
499    size_t getContentSize() const { return m_sourceFile->getContentSize(); }
500
501    /// Get the humane location
502    /// Type determines if the location wanted is the original, or the 'normal' (which modifys
503    /// behavior based on #line directives)
504    HumaneSourceLoc getHumaneLoc(SourceLoc loc, SourceLocType type = SourceLocType::Nominal);
505
506    /// Get the humane location, but store the path as a handle
507    HandleSourceLoc getHandleLoc(SourceLoc loc, SourceLocType type = SourceLocType::Nominal);
508
509
510    /// Get the path associated with a location
511    PathInfo getPathInfo(SourceLoc loc, SourceLocType type = SourceLocType::Nominal);
512
513    /// Get the initiating source location - that is the source location that caused the this
514    /// SourceView to be created Can be SourceLoc(0) if there is no initiating location. For example
515    /// for a #include - the view's initiating source loc for the view that is the contents of the
516    /// view will be the location of the #include in the source. For the original source file (ie
517    /// not an include) - the view will have an initiating source loc of SourceLoc(0)
518    SourceLoc getInitiatingSourceLoc() const { return m_initiatingSourceLoc; }
519
520    /// Gets the pathInfo for this view. It may be different from the m_sourceFile's if the path has
521    /// been overridden by m_viewPath
522    PathInfo getViewPathInfo() const;
523
524    /// Ctor
525    SourceView(
526        SourceFile* sourceFile,
527        SourceRange range,
528        const String* viewPath,
529        SourceLoc initiatingSourceLoc)
530        : m_range(range), m_sourceFile(sourceFile), m_initiatingSourceLoc(initiatingSourceLoc)
531    {
532        if (viewPath)
533        {
534            m_viewPath = *viewPath;
535        }
536    }
537
538protected:
539    /// Get the pathInfo from a string handle. If it's 0, it will return the _getPathInfo
540    PathInfo _getPathInfoFromHandle(StringSlicePool::Handle pathHandle) const;
541
542    SlangResult _findSourceMapLoc(SourceLoc loc, SourceLocType type, HandleSourceLoc& outLoc);
543
544    String m_viewPath; ///< Path to this view. If empty the path is the path to the SourceView
545
546    SourceLoc m_initiatingSourceLoc; ///< An optional source loc that defines where this view was
547                                     ///< initiated from. SourceLoc(0) if not defined.
548
549    SourceRange m_range;      ///< The range that this SourceView applies to
550    SourceFile* m_sourceFile; ///< The source file. Can hold the line breaks
551    List<Entry> m_entries;    ///< An array entries describing how we should interpret a range,
552                              ///< starting from the start location.
553    SourceLoc::RawValue m_absoluteLocationBase = 0; ///< Base of the absolute location mapping.
554    List<AbsoluteSegment> m_absSegments;            ///< Segments of absolute location mapping.
555};
556
557struct SourceManager
558{
559    // Initialize a source manager, with an optional parent
560    void initialize(SourceManager* parent, ISlangFileSystemExt* fileSystemExt);
561
562    /// Allocate a range of SourceLoc locations, these can be used to identify a specific location
563    /// in the source
564    SourceRange allocateSourceRange(UInt size);
565
566    /// Returns the loc for start of next allocation
567    SourceLoc getNextRangeStart() const { return m_nextLoc; }
568
569    /// Create a SourceFile defined with the specified path, and content held within a blob
570    SourceFile* createSourceFileWithSize(const PathInfo& pathInfo, size_t contentSize);
571    SourceFile* createSourceFileWithString(const PathInfo& pathInfo, const String& contents);
572    SourceFile* createSourceFileWithBlob(const PathInfo& pathInfo, ISlangBlob* blob);
573
574    /// Get the humane source location
575    HumaneSourceLoc getHumaneLoc(SourceLoc loc, SourceLocType type = SourceLocType::Nominal);
576
577    /// Get the path associated with a location
578    PathInfo getPathInfo(SourceLoc loc, SourceLocType type = SourceLocType::Nominal);
579
580    /// Create a new source view from a file
581    /// @param sourceFile is the source file that contains the source
582    /// @param pathInfo is path used to read the file from
583    /// @param initiatingSourceLoc the (optional) location in the source that led the the creation
584    /// of this view. If there isn't an initiating source location pass SourceLoc(0)s
585    SourceView* createSourceView(
586        SourceFile* sourceFile,
587        const PathInfo* pathInfo,
588        SourceLoc initiatingSourceLoc);
589
590    /// Find a view by a source file location.
591    /// If not found in this manager will look in the parent SourceManager
592    /// Returns nullptr if not found.
593    SourceView* findSourceViewRecursively(SourceLoc loc) const;
594
595    /// Find the SourceView associated with this manager for a specified location
596    /// Returns nullptr if not found.
597    SourceView* findSourceView(SourceLoc loc) const;
598
599    /// Searches this manager, and then the parent to see if can find a match for path.
600    /// If not found returns nullptr.
601    SourceFile* findSourceFileRecursively(const String& uniqueIdentity) const;
602    /// Find if the source file is defined on this manager.
603    SourceFile* findSourceFile(const String& uniqueIdentity) const;
604
605    /// Find a source file by path.
606    SourceFile* findSourceFileByPath(const String& name) const;
607    /// Find a source file by path recursively.
608    SourceFile* findSourceFileByPathRecursively(const String& name) const;
609
610    /// Searches this manager, and then the parent to see if can find a match
611    SourceFile* findSourceFileByContentRecursively(const char* text);
612    /// Find the source file that contains *the memory* text points to.
613    SourceFile* findSourceFileByContent(const char* text) const;
614
615    /// Get the file system associated with this source manager
616    ISlangFileSystemExt* getFileSystemExt() const { return m_fileSystemExt; }
617    /// Get the file system associated with this source manager
618    void setFileSystemExt(ISlangFileSystemExt* fileSystemExt) { m_fileSystemExt = fileSystemExt; }
619
620    /// Add a source file, uniqueIdentity must be unique for this manager AND any parents
621    void addSourceFile(const String& uniqueIdentity, SourceFile* sourceFile);
622    void addSourceFileIfNotExist(const String& uniqueIdentity, SourceFile* sourceFile);
623
624    // Maps a SourceLoc to an absolute location
625    SourceLoc::RawValue getAbsoluteLocation(SourceLoc location) const;
626
627    /// Get the slice pool
628    StringSlicePool& getStringSlicePool() { return m_slicePool; }
629
630    /// Get the source range for just this manager
631    /// Caution - the range will change if allocations are made to this manager.
632    SourceRange getSourceRange() const { return SourceRange(m_startLoc, m_nextLoc); }
633
634    /// Get the parent manager to this manager. Returns nullptr if there isn't any.
635    SourceManager* getParent() const { return m_parent; }
636
637    /// A memory arena to hold allocations that are in scope for the same time as SourceManager
638    MemoryArena* getMemoryArena() { return &m_memoryArena; }
639
640    /// Allocate a string slice
641    UnownedStringSlice allocateStringSlice(const UnownedStringSlice& slice);
642
643    /// Get all of the source files
644    const List<SourceFile*>& getSourceFiles() const { return m_sourceFiles; }
645
646    /// Get the source views
647    const List<SourceView*>& getSourceViews() const { return m_sourceViews; }
648
649    /// Resets state. Will release all views/source
650    void reset();
651
652    SourceManager()
653        : m_memoryArena(2048), m_slicePool(StringSlicePool::Style::Default)
654    {
655    }
656    ~SourceManager();
657
658protected:
659    void _resetLoc();
660    void _resetSource();
661
662    // The first location available to this source manager
663    // (may not be the first location of all, because we might
664    // have a parent source manager)
665    SourceLoc m_startLoc;
666
667    // The "parent" source manager that owns locations ahead of `startLoc`
668    SourceManager* m_parent = nullptr;
669
670    // The location to be used by the next source file to be loaded
671    SourceLoc m_nextLoc;
672
673    // All of the SourceViews constructed on this SourceManager. These are held in increasing order
674    // of range, so can find by doing a binary chop.
675    List<SourceView*> m_sourceViews;
676    // All of the SourceFiles constructed on this SourceManager. This owns the SourceFile.
677    List<SourceFile*> m_sourceFiles;
678
679    StringSlicePool m_slicePool;
680
681    // Memory arena that can be used for holding data to held in scope as long as the Source is
682    // Can be used for storing the decoded contents of Token. Content for example.
683    MemoryArena m_memoryArena;
684
685    // Maps uniqueIdentities to source files
686    Dictionary<String, SourceFile*> m_sourceFileMap;
687
688    ComPtr<ISlangFileSystemExt> m_fileSystemExt;
689};
690
691} // namespace Slang
692
693#endif