summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-source-loc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-source-loc.cpp')
-rw-r--r--source/slang/slang-source-loc.cpp591
1 files changed, 591 insertions, 0 deletions
diff --git a/source/slang/slang-source-loc.cpp b/source/slang/slang-source-loc.cpp
new file mode 100644
index 000000000..faa7e77c3
--- /dev/null
+++ b/source/slang/slang-source-loc.cpp
@@ -0,0 +1,591 @@
+// slang-source-loc.cpp
+#include "slang-source-loc.h"
+
+#include "slang-compiler.h"
+
+#include "../core/slang-string-util.h"
+
+namespace Slang {
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceView !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+const String PathInfo::getMostUniqueIdentity() const
+{
+ switch (type)
+ {
+ case Type::Normal: return uniqueIdentity;
+ case Type::FoundPath:
+ case Type::FromString:
+ {
+ return foundPath;
+ }
+ default: return "";
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceView !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+int SourceView::findEntryIndex(SourceLoc sourceLoc) const
+{
+ if (!m_range.contains(sourceLoc))
+ {
+ return -1;
+ }
+
+ const auto rawValue = sourceLoc.getRaw();
+
+ Index hi = m_entries.getCount();
+ // If there are no entries, or it is in front of the first entry, then there is no associated entry
+ if (hi == 0 ||
+ m_entries[0].m_startLoc.getRaw() > sourceLoc.getRaw())
+ {
+ return -1;
+ }
+
+ Index lo = 0;
+ while (lo + 1 < hi)
+ {
+ const Index mid = (hi + lo) >> 1;
+ const Entry& midEntry = m_entries[mid];
+ SourceLoc::RawValue midValue = midEntry.m_startLoc.getRaw();
+ if (midValue <= rawValue)
+ {
+ // The location we seek is at or after this entry
+ lo = mid;
+ }
+ else
+ {
+ // The location we seek is before this entry
+ hi = mid;
+ }
+ }
+
+ return int(lo);
+}
+
+void SourceView::addLineDirective(SourceLoc directiveLoc, StringSlicePool::Handle pathHandle, int line)
+{
+ SLANG_ASSERT(pathHandle != StringSlicePool::Handle(0));
+ SLANG_ASSERT(m_range.contains(directiveLoc));
+
+ // Check that the directiveLoc values are always increasing
+ SLANG_ASSERT(m_entries.getCount() == 0 || (m_entries.getLast().m_startLoc.getRaw() < directiveLoc.getRaw()));
+
+ // Calculate the offset
+ const int offset = m_range.getOffset(directiveLoc);
+
+ // Get the line index in the original file
+ const int lineIndex = m_sourceFile->calcLineIndexFromOffset(offset);
+
+ Entry entry;
+ entry.m_startLoc = directiveLoc;
+ entry.m_pathHandle = pathHandle;
+
+ // We also need to make sure that any lookups for line numbers will
+ // get corrected based on this files location.
+ // We assume the line number coming from the directive is a line number, NOT an index, so the correction needs + 1
+ // There is an additional + 1 because we want the NEXT line - ie the line after the #line directive, to the specified value
+ // Taking both into account means +2 is correct 'fix'
+ entry.m_lineAdjust = line - (lineIndex + 2);
+
+ m_entries.add(entry);
+}
+
+void SourceView::addLineDirective(SourceLoc directiveLoc, const String& path, int line)
+{
+ StringSlicePool::Handle pathHandle = getSourceManager()->getStringSlicePool().add(path.getUnownedSlice());
+ return addLineDirective(directiveLoc, pathHandle, line);
+}
+
+void SourceView::addDefaultLineDirective(SourceLoc directiveLoc)
+{
+ SLANG_ASSERT(m_range.contains(directiveLoc));
+ // Check that the directiveLoc values are always increasing
+ SLANG_ASSERT(m_entries.getCount() == 0 || (m_entries.getLast().m_startLoc.getRaw() < directiveLoc.getRaw()));
+
+ // Well if there are no entries, or the last one puts it in default case, then we don't need to add anything
+ if (m_entries.getCount() == 0 || (m_entries.getCount() && m_entries.getLast().isDefault()))
+ {
+ return;
+ }
+
+ Entry entry;
+ entry.m_startLoc = directiveLoc;
+ entry.m_lineAdjust = 0; // No line adjustment... we are going back to default
+ entry.m_pathHandle = StringSlicePool::Handle(0); // Mark that there is no path, and that this is a 'default'
+
+ SLANG_ASSERT(entry.isDefault());
+
+ m_entries.add(entry);
+}
+
+HumaneSourceLoc SourceView::getHumaneLoc(SourceLoc loc, SourceLocType type)
+{
+ const int offset = m_range.getOffset(loc);
+
+ // We need the line index from the original source file
+ const int lineIndex = m_sourceFile->calcLineIndexFromOffset(offset);
+
+ // TODO: we should really translate the byte index in the line
+ // to deal with:
+ //
+ // - Non-ASCII characters, while might consume multiple bytes
+ //
+ // - Tab characters, which should really adjust how we report
+ // columns (although how are we supposed to know the setting
+ // that an IDE expects us to use when reporting locations?)
+ const int columnIndex = m_sourceFile->calcColumnIndex(lineIndex, offset);
+
+ HumaneSourceLoc humaneLoc;
+ humaneLoc.column = columnIndex + 1;
+ humaneLoc.line = lineIndex + 1;
+
+ // Make up a default entry
+ StringSlicePool::Handle pathHandle = StringSlicePool::Handle(0);
+
+ // Only bother looking up the entry information if we want a 'Normal' lookup
+ const int entryIndex = (type == SourceLocType::Nominal) ? findEntryIndex(loc) : -1;
+ if (entryIndex >= 0)
+ {
+ const Entry& entry = m_entries[entryIndex];
+ // Adjust the line
+ humaneLoc.line += entry.m_lineAdjust;
+ // Get the pathHandle..
+ pathHandle = entry.m_pathHandle;
+ }
+
+ humaneLoc.pathInfo = _getPathInfoFromHandle(pathHandle);
+ return humaneLoc;
+}
+
+PathInfo SourceView::_getPathInfo() const
+{
+ if (m_viewPath.getLength())
+ {
+ PathInfo pathInfo(m_sourceFile->getPathInfo());
+ pathInfo.foundPath = m_viewPath;
+ return pathInfo;
+ }
+ else
+ {
+ return m_sourceFile->getPathInfo();
+ }
+}
+
+PathInfo SourceView::_getPathInfoFromHandle(StringSlicePool::Handle pathHandle) const
+{
+ // If there is no override path, then just the source files path
+ if (pathHandle == StringSlicePool::Handle(0))
+ {
+ return _getPathInfo();
+ }
+ else
+ {
+ return PathInfo::makePath(getSourceManager()->getStringSlicePool().getSlice(pathHandle));
+ }
+}
+
+PathInfo SourceView::getPathInfo(SourceLoc loc, SourceLocType type)
+{
+ if (type == SourceLocType::Actual)
+ {
+ return _getPathInfo();
+ }
+
+ const int entryIndex = findEntryIndex(loc);
+ return _getPathInfoFromHandle((entryIndex >= 0) ? m_entries[entryIndex].m_pathHandle : StringSlicePool::Handle(0));
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!! SourceFile !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void SourceFile::setLineBreakOffsets(const uint32_t* offsets, UInt numOffsets)
+{
+ m_lineBreakOffsets.clear();
+ m_lineBreakOffsets.addRange(offsets, numOffsets);
+}
+
+const List<uint32_t>& SourceFile::getLineBreakOffsets()
+{
+ // We now have a raw input file that we can search for line breaks.
+ // We obviously don't want to do a linear scan over and over, so we will
+ // cache an array of line break locations in the file.
+ if (m_lineBreakOffsets.getCount() == 0)
+ {
+ UnownedStringSlice content = getContent();
+
+ char const* begin = content.begin();
+ char const* end = content.end();
+
+ char const* cursor = begin;
+
+ // Treat the beginning of the file as a line break
+ m_lineBreakOffsets.add(0);
+
+ while (cursor != end)
+ {
+ int c = *cursor++;
+ switch (c)
+ {
+ case '\r': case '\n':
+ {
+ // When we see a line-break character we need
+ // to record the line break, but we also need
+ // to deal with the annoying issue of encodings,
+ // where a multi-byte sequence might encode
+ // the line break.
+
+ // Check to make sure that the EOF hasn't been reached.
+ if (cursor != end)
+ {
+ int d = *cursor;
+ if ((c ^ d) == ('\r' ^ '\n'))
+ cursor++;
+ }
+
+ m_lineBreakOffsets.add(uint32_t(cursor - begin));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Note that we do *not* treat the end of the file as a line
+ // break, because otherwise we would report errors like
+ // "end of file inside string literal" with a line number
+ // that points at a line that doesn't exist.
+ }
+
+ return m_lineBreakOffsets;
+}
+
+int SourceFile::calcLineIndexFromOffset(int offset)
+{
+ SLANG_ASSERT(UInt(offset) <= getContentSize());
+
+ // Make sure we have the line break offsets
+ const auto& lineBreakOffsets = getLineBreakOffsets();
+
+ // At this point we can assume the `lineBreakOffsets` array has been filled in.
+ // We will use a binary search to find the line index that contains our
+ // chosen offset.
+ Index lo = 0;
+ Index hi = lineBreakOffsets.getCount();
+
+ while (lo + 1 < hi)
+ {
+ const Index mid = (hi + lo) >> 1;
+ const uint32_t midOffset = lineBreakOffsets[mid];
+ if (midOffset <= uint32_t(offset))
+ {
+ lo = mid;
+ }
+ else
+ {
+ hi = mid;
+ }
+ }
+
+ return int(lo);
+}
+
+int SourceFile::calcColumnIndex(int lineIndex, int offset)
+{
+ const auto& lineBreakOffsets = getLineBreakOffsets();
+ return offset - lineBreakOffsets[lineIndex];
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceFile !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void SourceFile::setContents(ISlangBlob* blob)
+{
+ const UInt contentSize = blob->getBufferSize();
+
+ SLANG_ASSERT(contentSize == m_contentSize);
+
+ char const* contentBegin = (char const*)blob->getBufferPointer();
+ char const* contentEnd = contentBegin + contentSize;
+
+ m_contentBlob = blob;
+ m_content = UnownedStringSlice(contentBegin, contentEnd);
+}
+
+void SourceFile::setContents(const String& content)
+{
+ ComPtr<ISlangBlob> contentBlob = StringUtil::createStringBlob(content);
+ setContents(contentBlob);
+}
+
+SourceFile::SourceFile(SourceManager* sourceManager, const PathInfo& pathInfo, size_t contentSize) :
+ m_sourceManager(sourceManager),
+ m_pathInfo(pathInfo),
+ m_contentSize(contentSize)
+{
+}
+
+SourceFile::~SourceFile()
+{
+}
+
+String SourceFile::calcVerbosePath() const
+{
+ ISlangFileSystemExt* fileSystemExt = getSourceManager()->getFileSystemExt();
+
+ if (fileSystemExt)
+ {
+ String canonicalPath;
+ ComPtr<ISlangBlob> canonicalPathBlob;
+ if (SLANG_SUCCEEDED(fileSystemExt->getCanonicalPath(m_pathInfo.foundPath.getBuffer(), canonicalPathBlob.writeRef())))
+ {
+ canonicalPath = StringUtil::getString(canonicalPathBlob);
+ }
+ if (canonicalPath.getLength() > 0)
+ {
+ return canonicalPath;
+ }
+ }
+
+ return m_pathInfo.foundPath;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceManager !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void SourceManager::initialize(
+ SourceManager* p,
+ ISlangFileSystemExt* fileSystemExt)
+{
+ m_fileSystemExt = fileSystemExt;
+
+ m_parent = p;
+
+ if( p )
+ {
+ // If we have a parent source manager, then we assume that all code at that level
+ // has already been loaded, and it is safe to start our own source locations
+ // right after those from the parent.
+ //
+ // TODO: more clever allocation in cases where that might not be reasonable
+ m_startLoc = p->m_nextLoc;
+ }
+ else
+ {
+ // Location zero is reserved for an invalid location,
+ // so we need to start reserving locations starting at 1.
+ m_startLoc = SourceLoc::fromRaw(1);
+ }
+
+ m_nextLoc = m_startLoc;
+}
+
+SourceManager::~SourceManager()
+{
+ for (auto item : m_sourceViews)
+ {
+ delete item;
+ }
+
+ for (auto item : m_sourceFiles)
+ {
+ delete item;
+ }
+}
+
+UnownedStringSlice SourceManager::allocateStringSlice(const UnownedStringSlice& slice)
+{
+ const UInt numChars = slice.size();
+
+ char* dst = (char*)m_memoryArena.allocate(numChars);
+ ::memcpy(dst, slice.begin(), numChars);
+
+ return UnownedStringSlice(dst, numChars);
+}
+
+SourceRange SourceManager::allocateSourceRange(UInt size)
+{
+ // TODO: consider using atomics here
+
+
+ SourceLoc beginLoc = m_nextLoc;
+ SourceLoc endLoc = beginLoc + size;
+
+ // We need to be able to represent the location that is *at* the end of
+ // the input source, so the next available location for a new file
+ // must be placed one after the end of this one.
+
+ m_nextLoc = endLoc + 1;
+
+ return SourceRange(beginLoc, endLoc);
+}
+
+SourceFile* SourceManager::createSourceFileWithSize(const PathInfo& pathInfo, size_t contentSize)
+{
+ SourceFile* sourceFile = new SourceFile(this, pathInfo, contentSize);
+ m_sourceFiles.add(sourceFile);
+ return sourceFile;
+}
+
+SourceFile* SourceManager::createSourceFileWithString(const PathInfo& pathInfo, const String& contents)
+{
+ SourceFile* sourceFile = new SourceFile(this, pathInfo, contents.getLength());
+ m_sourceFiles.add(sourceFile);
+ sourceFile->setContents(contents);
+ return sourceFile;
+}
+
+SourceFile* SourceManager::createSourceFileWithBlob(const PathInfo& pathInfo, ISlangBlob* blob)
+{
+ SourceFile* sourceFile = new SourceFile(this, pathInfo, blob->getBufferSize());
+ m_sourceFiles.add(sourceFile);
+ sourceFile->setContents(blob);
+ return sourceFile;
+}
+
+SourceView* SourceManager::createSourceView(SourceFile* sourceFile, const PathInfo* pathInfo)
+{
+ SourceRange range = allocateSourceRange(sourceFile->getContentSize());
+
+ SourceView* sourceView = nullptr;
+ if (pathInfo &&
+ (pathInfo->foundPath.getLength() && sourceFile->getPathInfo().foundPath != pathInfo->foundPath))
+ {
+ sourceView = new SourceView(sourceFile, range, &pathInfo->foundPath);
+ }
+ else
+ {
+ sourceView = new SourceView(sourceFile, range, nullptr);
+ }
+
+ m_sourceViews.add(sourceView);
+
+ return sourceView;
+}
+
+SourceView* SourceManager::findSourceView(SourceLoc loc) const
+{
+ Index hi = m_sourceViews.getCount();
+ // It must be in the range of this manager and have associated views for it to possibly be a hit
+ if (!getSourceRange().contains(loc) || hi == 0)
+ {
+ return nullptr;
+ }
+
+ // If we don't have very many, we may as well just linearly search
+ if (hi <= 8)
+ {
+ for (int i = 0; i < hi; ++i)
+ {
+ SourceView* view = m_sourceViews[i];
+ if (view->getRange().contains(loc))
+ {
+ return view;
+ }
+ }
+ return nullptr;
+ }
+
+ const SourceLoc::RawValue rawLoc = loc.getRaw();
+
+ // Binary chop to see if we can find the associated SourceUnit
+ Index lo = 0;
+ while (lo + 1 < hi)
+ {
+ Index mid = (hi + lo) >> 1;
+
+ SourceView* midView = m_sourceViews[mid];
+ if (midView->getRange().contains(loc))
+ {
+ return midView;
+ }
+
+ const SourceLoc::RawValue midValue = midView->getRange().begin.getRaw();
+ if (midValue <= rawLoc)
+ {
+ // The location we seek is at or after this entry
+ lo = mid;
+ }
+ else
+ {
+ // The location we seek is before this entry
+ hi = mid;
+ }
+ }
+
+ // Check if low is actually a hit
+ SourceView* view = m_sourceViews[lo];
+ return (view->getRange().contains(loc)) ? view : nullptr;
+}
+
+SourceView* SourceManager::findSourceViewRecursively(SourceLoc loc) const
+{
+ // Start with this manager
+ const SourceManager* manager = this;
+ do
+ {
+ SourceView* sourceView = manager->findSourceView(loc);
+ // If we found a hit we are done
+ if (sourceView)
+ {
+ return sourceView;
+ }
+ // Try the parent
+ manager = manager->m_parent;
+ }
+ while (manager);
+ // Didn't find it
+ return nullptr;
+}
+
+SourceFile* SourceManager::findSourceFile(const String& uniqueIdentity) const
+{
+ SourceFile*const* filePtr = m_sourceFileMap.TryGetValue(uniqueIdentity);
+ return (filePtr) ? *filePtr : nullptr;
+}
+
+SourceFile* SourceManager::findSourceFileRecursively(const String& uniqueIdentity) const
+{
+ const SourceManager* manager = this;
+ do
+ {
+ SourceFile* sourceFile = manager->findSourceFile(uniqueIdentity);
+ if (sourceFile)
+ {
+ return sourceFile;
+ }
+ manager = manager->m_parent;
+ } while (manager);
+ return nullptr;
+}
+
+void SourceManager::addSourceFile(const String& uniqueIdentity, SourceFile* sourceFile)
+{
+ SLANG_ASSERT(!findSourceFileRecursively(uniqueIdentity));
+ m_sourceFileMap.Add(uniqueIdentity, sourceFile);
+}
+
+HumaneSourceLoc SourceManager::getHumaneLoc(SourceLoc loc, SourceLocType type)
+{
+ SourceView* sourceView = findSourceViewRecursively(loc);
+ if (sourceView)
+ {
+ return sourceView->getHumaneLoc(loc, type);
+ }
+ else
+ {
+ return HumaneSourceLoc();
+ }
+}
+
+PathInfo SourceManager::getPathInfo(SourceLoc loc, SourceLocType type)
+{
+ SourceView* sourceView = findSourceViewRecursively(loc);
+ if (sourceView)
+ {
+ return sourceView->getPathInfo(loc, type);
+ }
+ else
+ {
+ return PathInfo::makeUnknown();
+ }
+}
+
+} // namespace Slang