summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2023-04-17 15:09:37 -0400
committerGitHub <noreply@github.com>2023-04-17 15:09:37 -0400
commit90a9f43573ec0777c2ae4fa20c8fdc51a4ae7b3a (patch)
tree360750778be872a086674024a9ce5a68bf4e7cb3
parenta3f622ace1bdef1f1a4150ec85d1328d1a589333 (diff)
Round trip source map (#2810)
* #include an absolute path didn't work - because paths were taken to always be relative. * Make output of obfuscation locs work in a slang-module. * Tidy up detection for writing serialized source locs. * Support for .zip references. Handling of obfuscated source maps read from containers. A test to check obfuscated source map working on a module. * When using obfuscation, always obfuscate locs instead of stripping them. We keep a source map, so we can still produce reasonable errors. * Write out source locs if debug information is enabled. * Check output without sourcemap. * Small fixes. * Small improvements around hash calculation for source map name. * Disable test that fails on x86 gcc linux for now. * Fix issues around obfuscated source map using lines rather than columns. Fix some issues around encoding/decoding. * Make column calculation of source locs take into account utf8/tabs. Don't special case obfuscated source map for lookup for source loc. * Support following multiple source maps. * Small fixes/improvements around SourceMap lookup.
-rw-r--r--source/compiler-core/slang-artifact-container-util.cpp48
-rw-r--r--source/compiler-core/slang-artifact-container-util.h3
-rw-r--r--source/compiler-core/slang-artifact-desc-util.cpp10
-rw-r--r--source/compiler-core/slang-json-source-map-util.cpp90
-rw-r--r--source/compiler-core/slang-json-source-map-util.h4
-rw-r--r--source/compiler-core/slang-source-loc.cpp177
-rw-r--r--source/compiler-core/slang-source-loc.h34
-rw-r--r--source/slang/slang-compiler.cpp20
-rw-r--r--source/slang/slang-ir-obfuscate-loc.cpp118
-rw-r--r--source/slang/slang-lower-to-ir.cpp4
-rw-r--r--source/slang/slang-options.cpp6
-rw-r--r--source/slang/slang.cpp89
-rw-r--r--tests/serialization/obfuscated-loc-module.slang21
-rw-r--r--tests/serialization/obfuscated-module-check-loc.slang29
-rw-r--r--tests/serialization/obfuscated-module-check-loc.slang.1.expected6
-rw-r--r--tests/serialization/obfuscated-module-check-loc.slang.3.expected6
-rw-r--r--tests/serialization/obfuscated-serialized-module-test.slang2
17 files changed, 558 insertions, 109 deletions
diff --git a/source/compiler-core/slang-artifact-container-util.cpp b/source/compiler-core/slang-artifact-container-util.cpp
index 29e2e736e..6121df964 100644
--- a/source/compiler-core/slang-artifact-container-util.cpp
+++ b/source/compiler-core/slang-artifact-container-util.cpp
@@ -626,7 +626,6 @@ SlangResult ArtifactContainerReader::read(ISlangFileSystemExt* fileSystem, ComPt
return _readArtifactDirectory(0, outArtifact);
}
-
SlangResult ArtifactContainerReader::_readFile(Index fileIndex, ComPtr<IArtifact>& outArtifact)
{
outArtifact.setNull();
@@ -655,6 +654,19 @@ SlangResult ArtifactContainerReader::_readFile(Index fileIndex, ComPtr<IArtifact
return SLANG_OK;
}
+ // We don't have manifest, so for now well assume if the name ends in "-obfuscated" and it's a source map
+ // it's an obfuscated one
+ if (desc.kind == ArtifactKind::Json &&
+ desc.payload == ArtifactPayload::SourceMap)
+ {
+ auto name = Path::getFileNameWithoutExt(entry.name);
+
+ if (name.endsWith(toSlice("-obfuscated")))
+ {
+ desc.style = ArtifactStyle::Obfuscated;
+ }
+ }
+
// I guess I can just make an artifact for this
auto artifact = ArtifactUtil::createArtifact(desc);
@@ -781,6 +793,40 @@ SlangResult ArtifactContainerReader::_readArtifactDirectory(Index directoryIndex
return SLANG_OK;
}
+SlangResult ArtifactContainerUtil::readContainer(IArtifact* artifact, ComPtr<IArtifact>& outArtifact)
+{
+ auto desc = artifact->getDesc();
+
+ ComPtr<ISlangMutableFileSystem> fileSystem;
+
+ switch (desc.kind)
+ {
+ case ArtifactKind::Zip:
+ {
+ SLANG_RETURN_ON_FAIL(ZipFileSystem::create(fileSystem));
+
+ ComPtr<ISlangBlob> blob;
+ SLANG_RETURN_ON_FAIL(artifact->loadBlob(ArtifactKeep::No, blob.writeRef()));
+
+ // Load into the zip
+
+ // Now write out to the output file
+ IArchiveFileSystem* archiveFileSystem = as<IArchiveFileSystem>(fileSystem);
+ SLANG_ASSERT(archiveFileSystem);
+
+ SLANG_RETURN_ON_FAIL(archiveFileSystem->loadArchive(blob->getBufferPointer(), blob->getBufferSize()));
+ break;
+ }
+ default:
+ {
+ return SLANG_FAIL;
+ }
+ }
+
+ SLANG_RETURN_ON_FAIL(readContainer(fileSystem, outArtifact));
+ return SLANG_OK;
+}
+
/* static */SlangResult ArtifactContainerUtil::readContainer(ISlangFileSystemExt* fileSystem, ComPtr<IArtifact>& outArtifact)
{
SLANG_UNUSED(outArtifact);
diff --git a/source/compiler-core/slang-artifact-container-util.h b/source/compiler-core/slang-artifact-container-util.h
index b3e591797..c6e836245 100644
--- a/source/compiler-core/slang-artifact-container-util.h
+++ b/source/compiler-core/slang-artifact-container-util.h
@@ -33,6 +33,9 @@ struct ArtifactContainerUtil
static SlangResult writeContainer(IArtifact* artifact, const String& defaultFileName, ISlangMutableFileSystem* fileSystem);
static SlangResult readContainer(ISlangFileSystemExt* fileSystem, ComPtr<IArtifact>& outArtifact);
+
+ /// Read an artifact that represents a container as an artifact hierarchy
+ static SlangResult readContainer(IArtifact* artifact, ComPtr<IArtifact>& outArtifact);
};
} // namespace Slang
diff --git a/source/compiler-core/slang-artifact-desc-util.cpp b/source/compiler-core/slang-artifact-desc-util.cpp
index fb1c2dbd2..67501782a 100644
--- a/source/compiler-core/slang-artifact-desc-util.cpp
+++ b/source/compiler-core/slang-artifact-desc-util.cpp
@@ -460,7 +460,15 @@ static const KindExtension g_cpuKindExts[] =
/* static */bool ArtifactDescUtil::isLinkable(const ArtifactDesc& desc)
{
- if (isDerivedFrom(desc.kind, ArtifactKind::CompileBinary))
+ // If is a container with compile results *assume* that result is linkable
+ if (isDerivedFrom(desc.kind, ArtifactKind::Container) &&
+ isDerivedFrom(desc.payload, ArtifactPayload::CompileResults))
+ {
+ return true;
+ }
+
+ // if it's a compile binary or a container
+ if (isDerivedFrom(desc.kind, ArtifactKind::CompileBinary))
{
if (isDerivedFrom(desc.payload, ArtifactPayload::KernelLike))
{
diff --git a/source/compiler-core/slang-json-source-map-util.cpp b/source/compiler-core/slang-json-source-map-util.cpp
index a5a454bd5..3929a3387 100644
--- a/source/compiler-core/slang-json-source-map-util.cpp
+++ b/source/compiler-core/slang-json-source-map-util.cpp
@@ -105,34 +105,31 @@ static SlangResult _decode(UnownedStringSlice& ioEncoded, Index& out)
{
Index v = 0;
- Index shift = 0;
const char* cur = ioEncoded.begin();
const char* end = ioEncoded.end();
- // Must have some chars
- if (cur >= end)
{
- return SLANG_FAIL;
- }
-
- for (; cur < end; ++cur)
- {
- const Index value = g_vlqDecodeTable[*cur];
- if (value < 0)
+ Index shift = 0;
+ Index decodeValue = 0;
+ do
{
- return SLANG_FAIL;
- }
+ // Must have a char to decode
+ if (cur >= end)
+ {
+ return SLANG_FAIL;
+ }
+
+ decodeValue = g_vlqDecodeTable[*cur++];
+ if (decodeValue < 0)
+ {
+ return SLANG_FAIL;
+ }
- v += (value & 0x1f) << shift;
+ v += (decodeValue & 0x1f) << shift;
- // If the continuation bit is not set we are done
- if (( value & 0x20) == 0)
- {
- ++cur;
- break;
+ shift += 5;
}
-
- shift += 5;
+ while (decodeValue & 0x20);
}
// Save out the remaining part
@@ -158,20 +155,16 @@ void _encode(Index v, StringBuilder& out)
do
{
- // Encode it
- const auto nextV = v >> 5;
-
- // Encode 5 bits
- char c = g_vlqEncodeTable[(v & 0x1f)];
-
- // See what bits are remaining
- v = (v >> 5);
-
- // Set the continuation bit's if there is more to encode
- c |= v ? 0x20 : 0;
+ const Index nextV = v >> 5;
+ const Index encodeValue = (v & 0x1f) + (nextV ? 0x20 : 0);
+ // Encode 5 bits, plus continuation bit
+ char c = g_vlqEncodeTable[encodeValue];
+
// Save the char
*cur++ = c;
+
+ v = nextV;
}
while (v);
@@ -461,4 +454,39 @@ SlangResult JSONSourceMapUtil::encode(SourceMap* sourceMap, JSONContainer* conta
return SLANG_OK;
}
+SlangResult JSONSourceMapUtil::read(ISlangBlob* blob, DiagnosticSink* parentSink, RefPtr<SourceMap>& outSourceMap)
+{
+ SourceManager sourceManager;
+ sourceManager.initialize(nullptr, nullptr);
+ DiagnosticSink sink(&sourceManager, nullptr);
+
+ sink.setParentSink(parentSink);
+
+ RefPtr<JSONContainer> container = new JSONContainer(&sourceManager);
+
+ JSONValue rootValue;
+ {
+ // Now need to parse as JSON
+ SourceFile* sourceFile = sourceManager.createSourceFileWithBlob(PathInfo::makeUnknown(), blob);
+ SourceView* sourceView = sourceManager.createSourceView(sourceFile, nullptr, SourceLoc());
+
+ JSONLexer lexer;
+ lexer.init(sourceView, &sink);
+
+ JSONBuilder builder(container);
+
+ JSONParser parser;
+ SLANG_RETURN_ON_FAIL(parser.parse(&lexer, sourceView, &builder, &sink));
+
+ rootValue = builder.getRootValue();
+ }
+
+ RefPtr<SourceMap> sourceMap;
+
+ SLANG_RETURN_ON_FAIL(decode(container, rootValue, &sink, sourceMap));
+
+ outSourceMap = sourceMap;
+ return SLANG_OK;
+}
+
} // namespace Slang
diff --git a/source/compiler-core/slang-json-source-map-util.h b/source/compiler-core/slang-json-source-map-util.h
index 51b11b6cd..ba417dd7c 100644
--- a/source/compiler-core/slang-json-source-map-util.h
+++ b/source/compiler-core/slang-json-source-map-util.h
@@ -14,6 +14,10 @@ struct JSONSourceMapUtil
/// Converts the source map contents into JSON
static SlangResult encode(SourceMap* sourceMap, JSONContainer* container, DiagnosticSink* sink, JSONValue& outValue);
+
+ /// Read the blob (encoded as JSON) as a source map.
+ /// Sink is optional, and can be passed as nullptr
+ static SlangResult read(ISlangBlob* blob, DiagnosticSink* sink, RefPtr<SourceMap>& outSourceMap);
};
} // namespace Slang
diff --git a/source/compiler-core/slang-source-loc.cpp b/source/compiler-core/slang-source-loc.cpp
index 951f7feec..23cbdf839 100644
--- a/source/compiler-core/slang-source-loc.cpp
+++ b/source/compiler-core/slang-source-loc.cpp
@@ -3,6 +3,7 @@
#include "../core/slang-string-util.h"
#include "../core/slang-string-escape-util.h"
+#include "../core/slang-char-encode.h"
#include "slang-artifact-representation-impl.h"
#include "slang-artifact-impl.h"
@@ -189,44 +190,118 @@ void SourceView::addDefaultLineDirective(SourceLoc directiveLoc)
m_entries.add(entry);
}
-HandleSourceLoc SourceView::getHandleLoc(SourceLoc loc, SourceLocType type)
+SlangResult _findLocWithSourceMap(SourceManager* lookupSourceManager, SourceView* sourceView, SourceLoc loc, HandleSourceLoc& outLoc)
{
- auto obfuscatedSourceMap = getSourceFile()->getObfuscatedSourceMap();
- if (obfuscatedSourceMap)
+ auto sourceFile = sourceView->getSourceFile();
+
+ // Hold a list of sourceFiles visited so we can't end up in a loop of lookups
+ List<SourceFile*> sourceFiles;
+ sourceFiles.add(sourceFile);
+
+ Index entryIndex = -1;
+
+ // Do the initial lookup using the loc
+ {
+ const auto offset = sourceView->getRange().getOffset(loc);
+
+ const auto lineIndex = sourceFile->calcLineIndexFromOffset(offset);
+ const auto colIndex = sourceFile->calcColumnIndex(lineIndex, offset);
+
+ // If we are in this function the sourceFile should have a map
+ auto sourceMap = sourceFile->getSourceMap();
+ SLANG_ASSERT(sourceMap);
+
+ entryIndex = sourceMap->findEntry(lineIndex, colIndex);
+ }
+
+ if (entryIndex < 0)
{
- const Index col = getRange().getOffset(loc);
+ return SLANG_FAIL;
+ }
+
+ // Keep searching through source maps
+ do
+ {
+ auto sourceMap = sourceFile->getSourceMap();
- const Index entryIndex = obfuscatedSourceMap->findEntry(0, col);
- if (entryIndex >= 0)
+ // Find the entry
+ const auto& entry = sourceMap->getEntryByIndex(entryIndex);
+ const auto sourceFileName = sourceMap->getSourceFileName(entry.sourceFileIndex);
+
+ // If we have a source name, see if it already exists in source manager
+ if (sourceFileName.getLength())
{
- const auto& entry = obfuscatedSourceMap->getEntryByIndex(entryIndex);
+ if (auto foundSourceFile = lookupSourceManager->findSourceFileByPathRecursively(sourceFileName))
+ {
+ // We only follow if the source file hasn't already been visisted
+ if (sourceFiles.indexOf(foundSourceFile) < 0)
+ {
+ // Add so we don't reprocess
+ sourceFiles.add(foundSourceFile);
+
+ // If it has a source map, we try and look up the current location in it's source map
+ if (auto foundSourceMap = foundSourceFile->getSourceMap())
+ {
+ const auto foundEntryIndex = foundSourceMap->findEntry(entry.sourceLine, entry.sourceColumn);
+
+ // If we found the entry repeat the lookup
+ if (foundEntryIndex >= 0)
+ {
+ sourceFile = foundSourceFile;
+ entryIndex = foundEntryIndex;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ } while (false);
- // Generate the HandleSourceLoc
+ // Generate the HandleSourceLoc
+ auto sourceMap = sourceFile->getSourceMap();
+ const auto& entry = sourceMap->getEntryByIndex(entryIndex);
- HandleSourceLoc handleLoc;
- handleLoc.line = entry.sourceLine + 1;
- handleLoc.column = entry.sourceColumn + 1;
+ // We need to add the pool of the originating source view/file
+ const auto originatingSourceManager = sourceView->getSourceManager();
- auto& managerPool = getSourceManager()->getStringSlicePool();
+ auto& managerPool = originatingSourceManager->getStringSlicePool();
- handleLoc.pathHandle = managerPool.add(obfuscatedSourceMap->getSourceFileName(entry.sourceFileIndex));
+ outLoc.line = entry.sourceLine + 1;
+ outLoc.column = entry.sourceColumn + 1;
+ outLoc.pathHandle = managerPool.add(sourceMap->getSourceFileName(entry.sourceFileIndex));
+
+ return SLANG_OK;
+}
+
+HandleSourceLoc SourceView::getHandleLoc(SourceLoc loc, SourceLocType type)
+{
+ // If it's nominal
+ if (type == SourceLocType::Nominal && m_sourceFile->getSourceMap())
+ {
+ // TODO(JS):
+ // Ideally we'd do the lookup on the "current" source manager rather than the source manager on this
+ // view, which may be a parent to the current one.
+ auto lookupSourceManager = m_sourceFile->getSourceManager();
+
+ HandleSourceLoc handleLoc;
+ if (SLANG_SUCCEEDED(_findLocWithSourceMap(lookupSourceManager, this, loc, handleLoc)))
+ {
return handleLoc;
}
}
+ // Get the offset in bytes for this loc
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
- //
+ // TODO:
// - 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?)
+ // that an IDE expects us to use when reporting locations?)
+ //
+ // For now we just count tabs as single chars
const int columnIndex = m_sourceFile->calcColumnIndex(lineIndex, offset);
HandleSourceLoc handleLoc;
@@ -409,12 +484,43 @@ int SourceFile::calcLineIndexFromOffset(int offset)
return int(lo);
}
-int SourceFile::calcColumnIndex(int lineIndex, int offset)
+int SourceFile::calcColumnOffset(int lineIndex, int offset)
{
const auto& lineBreakOffsets = getLineBreakOffsets();
return offset - lineBreakOffsets[lineIndex];
}
+int SourceFile::calcColumnIndex(int lineIndex, int offset, int tabSize)
+{
+ const int colOffset = calcColumnOffset(lineIndex, offset);
+
+ // If we don't have the content of the file, the best we can do is to assume there is a char per column
+ if (!hasContent())
+ {
+ return colOffset;
+ }
+
+ const auto line = getLineAtIndex(lineIndex);
+
+ const auto head = line.head(colOffset);
+
+ auto colCount = UTF8Util::calcCodePointCount(head);
+
+ if (tabSize >= 0)
+ {
+ Count tabCount = 0;
+ for (auto c : head)
+ {
+ tabCount += Count(c == '\t');
+ }
+
+ // We substract one from tabSize, because colCount will already holds a +1 for each tab.
+ colCount += tabCount * (tabSize - 1);
+ }
+
+ return int(colCount);
+}
+
/* !!!!!!!!!!!!!!!!!!!!!!!!! SourceFile !!!!!!!!!!!!!!!!!!!!!!!!!!!! */
void SourceFile::setContents(ISlangBlob* blob)
@@ -677,6 +783,37 @@ SourceView* SourceManager::findSourceViewRecursively(SourceLoc loc) const
return nullptr;
}
+SourceFile* SourceManager::findSourceFileByPathRecursively(const String& name) const
+{
+ // Start with this manager
+ const SourceManager* manager = this;
+ do
+ {
+ SourceFile* sourceFile = manager->findSourceFileByPath(name);
+ // If we found a hit we are done
+ if (sourceFile)
+ {
+ return sourceFile;
+ }
+ // Try the parent
+ manager = manager->m_parent;
+ } while (manager);
+ // Didn't find it
+ return nullptr;
+}
+
+SourceFile* SourceManager::findSourceFileByPath(const String& name) const
+{
+ for(auto sourceFile : m_sourceFiles)
+ {
+ if (sourceFile->getPathInfo().foundPath == name)
+ {
+ return sourceFile;
+ }
+ }
+ return nullptr;
+}
+
SourceFile* SourceManager::findSourceFile(const String& uniqueIdentity) const
{
SourceFile*const* filePtr = m_sourceFileMap.TryGetValue(uniqueIdentity);
diff --git a/source/compiler-core/slang-source-loc.h b/source/compiler-core/slang-source-loc.h
index 4103c9d6d..98b1bdbbd 100644
--- a/source/compiler-core/slang-source-loc.h
+++ b/source/compiler-core/slang-source-loc.h
@@ -218,8 +218,13 @@ public:
/// Calculate the line based on the offset
int calcLineIndexFromOffset(int offset);
- /// Calculate the offset for a line
- int calcColumnIndex(int line, int offset);
+ /// Calculate the offset (in bytes) for a line
+ int calcColumnOffset(int line, int offset);
+
+ /// Given a line and offset (in bytes for the whole file), return the column index, taking into account tabs
+ /// and utf8 encoding.
+ /// Passing tabSize uses the default tab size (currently tab set to 1)
+ int calcColumnIndex(int line, int offset, int tabSize = -1);
/// Get the content holding blob
ISlangBlob* getContentBlob() const { return m_contentBlob; }
@@ -247,11 +252,11 @@ public:
/// Get the source manager this was created on
SourceManager* getSourceManager() const { return m_sourceManager; }
- /// If set this "file" only exists as a way to obfuscate locations
- /// The mapping between the two is specified in the specified source map
- SourceMap* getObfuscatedSourceMap() const { return m_obfuscatedSourceMap; }
- /// Set the obfuscated source map
- void setObfuscatedSourceMap(SourceMap* sourceMap) { m_obfuscatedSourceMap = sourceMap; }
+ /// Get the source map associated with this file. If it's set when doing
+ /// lookup for source locations, the source map will be used
+ SourceMap* getSourceMap() const { return m_sourceMap; }
+ /// Set a source map
+ void setSourceMap(SourceMap* sourceMap) { m_sourceMap = sourceMap; }
/// Ctor
SourceFile(SourceManager* sourceManager, const PathInfo& pathInfo, size_t contentSize);
@@ -272,15 +277,15 @@ public:
// the input file:
List<uint32_t> m_lineBreakOffsets;
- // If set then this file isn't a regular source file, but provides obfuscation.
- // The mapping of that obfuscation can be found via the obfuscated source map
- RefPtr<SourceMap> m_obfuscatedSourceMap;
+ // If set then the locations in this file are really from locations from elsewhere,
+ // where the SourceMap specifies that mapping
+ RefPtr<SourceMap> m_sourceMap;
};
enum class SourceLocType
{
- Nominal, ///< The normal interpretation which takes into account #line directives
- Actual, ///< Ignores #line directives - and is the location as seen in the actual file
+ Nominal, ///< The normal interpretation which takes into account #line directives and source maps
+ Actual, ///< Ignores #line directives/source maps - and is the location as seen in the actual file
};
// A source location in a format a human might like to see
@@ -441,6 +446,11 @@ struct SourceManager
/// Find if the source file is defined on this manager.
SourceFile* findSourceFile(const String& uniqueIdentity) const;
+ /// Find a source file by path.
+ SourceFile* findSourceFileByPath(const String& name) const;
+ /// Find a source file by path recursively.
+ SourceFile* findSourceFileByPathRecursively(const String& name) const;
+
/// Searches this manager, and then the parent to see if can find a match
SourceFile* findSourceFileByContentRecursively(const char* text);
/// Find the source file that contains *the memory* text points to.
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index 43a6d238e..becd65596 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -1812,6 +1812,19 @@ namespace Slang
}
+ bool _shouldWriteSourceLocs(Linkage* linkage)
+ {
+ // If debug information or source manager are not avaiable we can't/shouldn't write out locs
+ if (linkage->debugInfoLevel == DebugInfoLevel::None ||
+ linkage->getSourceManager() == nullptr)
+ {
+ return false;
+ }
+
+ // Otherwise we do want to write out the locs
+ return true;
+ }
+
SlangResult EndToEndCompileRequest::writeContainerToStream(Stream* stream)
{
auto linkage = getLinkage();
@@ -1827,7 +1840,9 @@ namespace Slang
// Also currently only IR is needed.
options.optionFlags &= ~SerialOptionFlag::ASTModule;
}
- else if (linkage->debugInfoLevel != DebugInfoLevel::None && linkage->getSourceManager())
+
+ // If debug information is enabled, enable writing out source locs
+ if (_shouldWriteSourceLocs(linkage))
{
options.optionFlags |= SerialOptionFlag::SourceLocation;
options.sourceManager = linkage->getSourceManager();
@@ -1897,7 +1912,8 @@ namespace Slang
auto sourceMap = translationUnit->getModule()->getIRModule()->getObfuscatedSourceMap();
- if (sourceMap)
+ // If we have a source map *and* we want to generate them for output add to the container
+ if (sourceMap && getLinkage()->m_generateSourceMap)
{
// Write it out
String json;
diff --git a/source/slang/slang-ir-obfuscate-loc.cpp b/source/slang/slang-ir-obfuscate-loc.cpp
index 3ff2f3713..9a2f15fa4 100644
--- a/source/slang/slang-ir-obfuscate-loc.cpp
+++ b/source/slang/slang-ir-obfuscate-loc.cpp
@@ -46,6 +46,17 @@ static void _findInstsRec(IRInst* inst, List<InstWithLoc>& out)
}
}
+// We assume the root source manager is the stdlibs
+static SourceLoc _getStdLibLastLoc(SourceManager* sourceManager)
+{
+ auto rootManager = sourceManager;
+ while (rootManager->getParent())
+ {
+ rootManager = rootManager->getParent();
+ }
+ return rootManager->getNextRangeStart();
+}
+
SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager)
{
// There shouldn't be an obfuscated source map set
@@ -60,26 +71,28 @@ SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager)
instWithLocs.sort();
// Lets produce a hash, so we can use as a key for random number generation.
- // We could base it on time, or some other thing as there is no requirement for
- // stability or consistency.
- // We use a hash because it avoids issues around clocks, and availability of a clock
- // as a good source of entropy.
- //
- // An argument *could* be made to generate the name via some mechanism that uniquely identified the
- // combination of flags, options, files, names that identified the compilation, but that is
- // not easily achieved.
+ //
+ // We could base it on time, or some other random seed. But it would be preferable
+ // if it was stable, and compilations of the same module on different machines
+ // produce the same hash.
+ //
+ // Doing so would mean that we could use the obfuscated location ouput to output
+ // the origin.
+
HashCode hash = 0;
List<LocPair> locPairs;
// We want the hash to be stable. One problem is the source locs depend on their order of inclusion.
- // To work around this we are going
+ // To work around this we are going to hash via offsets, not locs.
{
SourceView* sourceView = nullptr;
+ const SourceLoc endStdLibLoc = _getStdLibLastLoc(sourceManager);
+
SourceLoc curLoc;
for (const auto& instWithLoc : instWithLocs)
- {
+ {
if (instWithLoc.loc != curLoc)
{
LocPair locPair;
@@ -89,21 +102,36 @@ SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager)
// This is the current loc
curLoc = instWithLoc.loc;
+ // Ignore any stdlib locs in the hash
+ if (instWithLoc.loc.getRaw() < endStdLibLoc.getRaw())
+ {
+ continue;
+ }
+
// If the loc isn't in the view, lookup the view it is in
if (sourceView == nullptr ||
!sourceView->getRange().contains(curLoc))
{
sourceView = sourceManager->findSourceViewRecursively(curLoc);
SLANG_ASSERT(sourceView);
-
+ // If there is no source view we can't apply to the hash
+ if (sourceView == nullptr)
+ {
+ continue;
+ }
+
+ const auto pathInfo = sourceView->getViewPathInfo();
+ const auto name = pathInfo.getName();
+ const auto nameHash = getHashCode(pathInfo.getName().getUnownedSlice());
+
// Combine the name
- hash = combineHash(hash, getHashCode(sourceView->getViewPathInfo().getName().getUnownedSlice()));
+ hash = combineHash(hash, nameHash);
}
- SLANG_ASSERT(sourceView);
// We combine the *offset* which is stable
- hash = combineHash(hash, getHashCode(sourceView->getRange().getOffset(curLoc)));
- }
+ const auto offset = sourceView->getRange().getOffset(curLoc);
+ hash = combineHash(hash, getHashCode(offset));
+ }
}
}
@@ -138,16 +166,28 @@ SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager)
dst[i * 2 + 1] = CharUtil::getHexChar(data[i] >> 4);
}
buf.appendInPlace(dst, charsCount);
+
+ // Make it clear this "source" is actually just for obfuscation.
+ buf << toSlice("-obfuscated");
+
obfusctatedPathInfo = PathInfo::makePath(buf);
}
}
SourceFile* obfuscatedFile = sourceManager->createSourceFileWithSize(obfusctatedPathInfo, uniqueLocCount);
- // We have only one line for all locs, just set up that way...
+ // We put each loc on it's own line. We do this rather than using a single line because
+ // it means the `#line` directives can still do something meaningful, since the best resolution
+ // they have is a single line.
{
- const uint32_t offsets[2] = { 0, uint32_t(uniqueLocCount) };
- obfuscatedFile->setLineBreakOffsets(offsets, SLANG_COUNT_OF(offsets));
+ List<uint32_t> offsets;
+ offsets.setCount(uniqueLocCount + 1);
+ for (Index i = 0; i < uniqueLocCount + 1; ++i)
+ {
+ offsets[i] = uint32_t(i);
+ }
+
+ obfuscatedFile->setLineBreakOffsets(offsets.getBuffer(), offsets.getCount());
}
// Create the view we are going to use from the obfusctated "file".
@@ -210,10 +250,16 @@ SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager)
RefPtr<SourceMap> sourceMap = new SourceMap;
sourceMap->m_file = obfusctatedPathInfo.getName();
- // Make sure we have line 0.
- // We only end up with one line in the obfuscated map.
- sourceMap->advanceToLine(0);
-
+ // Set up entries one per line
+ List<SourceMap::Entry> entries;
+ {
+ entries.setCount(uniqueLocCount);
+ for (auto& entry : entries)
+ {
+ entry.init();
+ }
+ }
+
{
// Current view, with cached "View" based sourceFileIndex
SourceView* curView = nullptr;
@@ -275,27 +321,35 @@ SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager)
sourceFileIndex = curPathSourceFileIndex;
}
- // Create the entry
- SourceMap::Entry entry;
- entry.init();
+ // Calculate the line index associated with this loc
+ const Index generatedLineIndex = Index(obfuscatedRange.getOffset(pair.obfuscatedLoc));
+
+ // Set it up
+ SourceMap::Entry& entry = entries[generatedLineIndex];
entry.sourceFileIndex = sourceFileIndex;
- // Calculate the column offset, from the pair obfuscated loc
- entry.generatedColumn = Index(obfuscatedRange.getOffset(pair.obfuscatedLoc));
-
+ // The generated has a line per loc, so the generated column is always 0
+ entry.generatedColumn = 0;
+
// We need to subtract 1, because handleLoc locations are 1 indexed, but SourceMap
// entry is 0 indexed.
entry.sourceColumn = handleLoc.column - 1;
entry.sourceLine = handleLoc.line - 1;
-
- // Add it to the source map
- sourceMap->addEntry(entry);
}
}
+ // Add all of the entries in line order to the source map
+ for (Index i = 0; i < uniqueLocCount; ++i)
+ {
+ // Advance to the current line.
+ sourceMap->advanceToLine(i);
+ // Add it to the source map
+ sourceMap->addEntry(entries[i]);
+ }
+
// Associate the sourceMap with the obfuscated file
- obfuscatedFile->setObfuscatedSourceMap(sourceMap);
+ obfuscatedFile->setSourceMap(sourceMap);
// Set the obfuscated map onto the module
module->setObfuscatedSourceMap(sourceMap);
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 8cceaff02..110f85b87 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -9652,7 +9652,7 @@ RefPtr<IRModule> generateIRForTranslationUnit(
// We don't do the obfuscation remapping here, because DCE and other passes may
// change what locs are actually needed, we need to be sure
// that if we have obfuscation enabled we don't forget to obfuscate.
- stripOptions.stripSourceLocs = linkage->m_obfuscateCode && !linkage->m_generateSourceMap;
+ stripOptions.stripSourceLocs = false;
stripFrontEndOnlyInstructions(module, stripOptions);
// Stripping out decorations could leave some dead code behind
@@ -9667,7 +9667,7 @@ RefPtr<IRModule> generateIRForTranslationUnit(
options.keepExportsAlive = true;
eliminateDeadCode(module, options);
- if (linkage->m_obfuscateCode && linkage->m_generateSourceMap)
+ if (linkage->m_obfuscateCode)
{
// The obfuscated source map is stored on the module
obfuscateModuleLocs(module, compileRequest->getSourceManager());
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index ae7988322..b1e1b93d2 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -1623,6 +1623,12 @@ struct OptionsParser
desc.kind = ArtifactKind::Library;
}
+ // If its a zip we'll *assume* its a zip holding compilation results
+ if (desc.kind == ArtifactKind::Zip)
+ {
+ desc.payload = ArtifactPayload::CompileResults;
+ }
+
if (!ArtifactDescUtil::isLinkable(desc))
{
sink->diagnose(referenceModuleName.loc, Diagnostics::kindNotLinkable, Path::getPathExt(path));
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index 4904abe03..c8cda0dca 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -15,6 +15,8 @@
#include "../compiler-core/slang-artifact-associated-impl.h"
#include "../compiler-core/slang-artifact-container-util.h"
+#include "../compiler-core/slang-json-source-map-util.h"
+
#include "../core/slang-memory-file-system.h"
#include "slang-module-library.h"
@@ -4833,10 +4835,85 @@ void EndToEndCompileRequest::setDefaultModuleName(const char* defaultModuleName)
frontEndReq->m_defaultModuleName = namePool->getName(defaultModuleName);
}
+SlangResult _addLibraryReference(EndToEndCompileRequest* req, IArtifact* artifact, ModuleLibrary* moduleLibrary)
+{
+ FrontEndCompileRequest* frontEndRequest = req->getFrontEndReq();
+ frontEndRequest->m_extraEntryPoints.addRange(moduleLibrary->m_entryPoints.getBuffer(), moduleLibrary->m_entryPoints.getCount());
+
+ // Add to the m_libModules
+ auto linkage = req->getLinkage();
+ linkage->m_libModules.add(ComPtr<IArtifact>(artifact));
+
+ return SLANG_OK;
+}
+
SlangResult _addLibraryReference(EndToEndCompileRequest* req, IArtifact* artifact)
{
auto desc = artifact->getDesc();
+ // TODO(JS):
+ // This isn't perhaps the best way to handle this scenario, as IArtifact can
+ // support lazy evaluation, with suitable hander.
+ // For now we just read in and strip out the bits we want.
+ if (isDerivedFrom(desc.kind, ArtifactKind::Container) &&
+ isDerivedFrom(desc.payload, ArtifactPayload::CompileResults))
+ {
+ // We want to read as a file system
+ ComPtr<IArtifact> container;
+
+ SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::readContainer(artifact, container));
+
+ // Find the payload... It should be linkable
+ if (!ArtifactDescUtil::isLinkable(container->getDesc()))
+ {
+ return SLANG_FAIL;
+ }
+
+ ComPtr<IModuleLibrary> libraryIntf;
+ SLANG_RETURN_ON_FAIL(loadModuleLibrary(ArtifactKeep::Yes, container, req, libraryIntf));
+
+ auto library = as<ModuleLibrary>(libraryIntf);
+
+ // Look for source maps
+ for (auto associated : container->getAssociated())
+ {
+ auto assocDesc = associated->getDesc();
+
+ // If we find an obfuscated source map load it and associate
+ if (isDerivedFrom(assocDesc.kind, ArtifactKind::Json) &&
+ isDerivedFrom(assocDesc.payload, ArtifactPayload::SourceMap) &&
+ isDerivedFrom(assocDesc.style, ArtifactStyle::Obfuscated))
+ {
+ ComPtr<ISlangBlob> sourceMapBlob;
+ SLANG_RETURN_ON_FAIL(associated->loadBlob(ArtifactKeep::No, sourceMapBlob.writeRef()));
+
+ RefPtr<SourceMap> sourceMap;
+ SLANG_RETURN_ON_FAIL(JSONSourceMapUtil::read(sourceMapBlob, nullptr, sourceMap));
+
+ // I guess we add to all ir modules?
+
+ for (auto irModule : library->m_modules)
+ {
+ irModule->setObfuscatedSourceMap(sourceMap);
+ }
+
+ // Look up the source file
+ auto sourceManager = req->getSink()->getSourceManager();
+
+ auto name = Path::getFileNameWithoutExt(associated->getName());
+
+ if (name.getLength())
+ {
+ auto sourceFile = sourceManager->findSourceFileByPathRecursively(name);
+ sourceFile->setSourceMap(sourceMap);
+ }
+ }
+ }
+
+ SLANG_RETURN_ON_FAIL(_addLibraryReference(req, container, library));
+ return SLANG_OK;
+ }
+
if (desc.kind == ArtifactKind::Library && desc.payload == ArtifactPayload::SlangIR)
{
ComPtr<IModuleLibrary> libraryIntf;
@@ -4849,15 +4926,13 @@ SlangResult _addLibraryReference(EndToEndCompileRequest* req, IArtifact* artifac
return SLANG_FAIL;
}
- FrontEndCompileRequest* frontEndRequest = req->getFrontEndReq();
- frontEndRequest->m_extraEntryPoints.addRange(library->m_entryPoints.getBuffer(), library->m_entryPoints.getCount());
- }
- else
- {
- // TODO(JS):
- // Do we want to check the path exists?
+ SLANG_RETURN_ON_FAIL(_addLibraryReference(req, artifact, library));
+ return SLANG_OK;
}
+ // TODO(JS):
+ // Do we want to check the path exists?
+
// Add to the m_libModules
auto linkage = req->getLinkage();
linkage->m_libModules.add(ComPtr<IArtifact>(artifact));
diff --git a/tests/serialization/obfuscated-loc-module.slang b/tests/serialization/obfuscated-loc-module.slang
new file mode 100644
index 000000000..63c4f61ee
--- /dev/null
+++ b/tests/serialization/obfuscated-loc-module.slang
@@ -0,0 +1,21 @@
+//TEST_IGNORE_FILE:
+
+// obfuscated-loc-module.slang
+
+
+int billy(int v)
+{
+ return v + 1;
+}
+
+// This function is designed to fail during IR passes/emit.
+int silly(int a)
+{
+ int t = 0;
+ [ForceUnroll(10)]
+ while ( a > 0)
+ {
+ t = t + t + a;
+ }
+ return t;
+}
diff --git a/tests/serialization/obfuscated-module-check-loc.slang b/tests/serialization/obfuscated-module-check-loc.slang
new file mode 100644
index 000000000..f603d5403
--- /dev/null
+++ b/tests/serialization/obfuscated-module-check-loc.slang
@@ -0,0 +1,29 @@
+//TEST:COMPILE: tests/serialization/obfuscated-loc-module.slang -o tests/serialization/obfuscated-loc-module.zip -g -obfuscate -source-map
+//TEST:SIMPLE:-target hlsl -stage compute -entry computeMain -obfuscate -r tests/serialization/obfuscated-loc-module.zip
+//TEST:COMPILE: tests/serialization/obfuscated-loc-module.slang -o tests/serialization/obfuscated-loc-module.zip -g -obfuscate
+// Disable for now as it breaks on gcc/release as different hash seems to be produced
+//DISABLE_TEST:SIMPLE:-target hlsl -stage compute -entry computeMain -obfuscate -r tests/serialization/obfuscated-loc-module.zip
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<float> outputBuffer;
+
+// This test checks obfuscated source map loc tracking through a round trip, and producing a location correctly from slang-module that has a source map
+
+// We *don't* import because if we do we'll get a fresh compilation from source... we want to make sure it's using the -r module
+//import obfuscated_loc_module;
+int silly(int v);
+int billy(int v);
+
+[numthreads(1, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ int x = int(dispatchThreadID.x);
+
+ x = billy(x);
+
+ // Will produce an error, because silly has an error.
+ int v = silly(x);
+
+ outputBuffer[x] = v;
+}
+
diff --git a/tests/serialization/obfuscated-module-check-loc.slang.1.expected b/tests/serialization/obfuscated-module-check-loc.slang.1.expected
new file mode 100644
index 000000000..aaf3bfd48
--- /dev/null
+++ b/tests/serialization/obfuscated-module-check-loc.slang.1.expected
@@ -0,0 +1,6 @@
+result code = -1
+standard error = {
+tests/serialization/obfuscated-loc-module.slang(16): error 40020: loop does not terminate within the limited number of iterations, unrolling is aborted.
+}
+standard output = {
+}
diff --git a/tests/serialization/obfuscated-module-check-loc.slang.3.expected b/tests/serialization/obfuscated-module-check-loc.slang.3.expected
new file mode 100644
index 000000000..94f740eb5
--- /dev/null
+++ b/tests/serialization/obfuscated-module-check-loc.slang.3.expected
@@ -0,0 +1,6 @@
+result code = -1
+standard error = {
+bc65f637-obfuscated(6): error 40020: loop does not terminate within the limited number of iterations, unrolling is aborted.
+}
+standard output = {
+}
diff --git a/tests/serialization/obfuscated-serialized-module-test.slang b/tests/serialization/obfuscated-serialized-module-test.slang
index 55a68f6c2..b007b7516 100644
--- a/tests/serialization/obfuscated-serialized-module-test.slang
+++ b/tests/serialization/obfuscated-serialized-module-test.slang
@@ -3,7 +3,7 @@
// A test to try out the basics of module
// serialization, obfuscation and source maps.
-//TEST:COMPILE: tests/serialization/serialized-module.slang -o tests/serialization/obfuscated-serialized-module.slang-module -obfuscate -source-map
+//TEST:COMPILE: tests/serialization/serialized-module.slang -o tests/serialization/obfuscated-serialized-module.slang-module -g -obfuscate -source-map
//TEST:COMPARE_COMPUTE_EX:-slang -compute -Xslang... -r tests/serialization/obfuscated-serialized-module.slang-module -obfuscate -source-map -X. -shaderobj
//import obfuscated_serialized_module;