https://github.com/shader-slang/slang
Tip revision: 5902acdabc4445a65741a7a6a3a95f223e301059 authored by Yong He on 23 January 2024, 07:19:40 UTC
[LSP] Fetch configs directly from didConfigurationChanged message. (#3478)
[LSP] Fetch configs directly from didConfigurationChanged message. (#3478)
Tip revision: 5902acd
slang-repro.cpp
// slang-repro.cpp
#include "slang-repro.h"
#include "../core/slang-text-io.h"
#include "../core/slang-stream.h"
#include "../core/slang-math.h"
#include "../core/slang-type-text-util.h"
#include "../core/slang-castable.h"
#include "slang-options.h"
#include "../compiler-core/slang-artifact-util.h"
#include "../compiler-core/slang-artifact-desc-util.h"
#include "../compiler-core/slang-source-loc.h"
namespace Slang {
/* static */const RiffSemanticVersion ReproUtil::g_semanticVersion =
RiffSemanticVersion::make(ReproUtil::kMajorVersion, ReproUtil::kMinorVersion, ReproUtil::kPatchVersion);
// We can't just use sizeof for the sizes of these types, because the hash will be dependent on the ptr size,
// which isn't an issue for serialization (we turn all pointers into Offset32Ptr -> uint32_t). So we use an x macro
// to set up the thing to hash.
//
// Note that bool is in the list because size of bool can change between compilers.
#define SLANG_STATE_TYPES(x) \
x(Util::FileState) \
x(Util::PathInfoState) \
x(Util::PathInfoState::CompressedResult) \
x(SlangPathType) \
x(Util::PathAndPathInfo) \
x(Util::TargetRequestState) \
x(Profile) \
x(CodeGenTarget) \
x(SlangTargetFlags) \
x(FloatingPointMode) \
x(Util::StringPair) \
x(Util::SourceFileState) \
x(PathInfo::Type) \
x(Util::TranslationUnitRequestState) \
x(SourceLanguage) \
x(Util::EntryPointState) \
x(Profile) \
x(Util::RequestState) \
x(SlangCompileFlags) \
x(bool) \
x(LineDirectiveMode) \
x(DebugInfoLevel) \
x(OptimizationLevel) \
x(ContainerFormat) \
x(PassThroughMode) \
x(SlangMatrixLayoutMode) \
#define SLANG_STATE_TYPE_SIZE(x) uint32_t(sizeof(x)),
// A function to calculate the hash related in list in part to how the types used are sized. Can catch crude breaking binary differences.
static StableHashCode32 _calcTypeHash()
{
typedef ReproUtil Util;
const uint32_t sizes[] =
{
SLANG_STATE_TYPES(SLANG_STATE_TYPE_SIZE)
};
return getStableHashCode32((const char*)&sizes, sizeof(sizes));
}
static StableHashCode32 _getTypeHash()
{
static StableHashCode32 s_hash = _calcTypeHash();
return s_hash;
}
namespace { // anonymous
struct StoreContext
{
typedef ReproUtil::FileState FileState;
typedef ReproUtil::SourceFileState SourceFileState;
typedef ReproUtil::PathInfoState PathInfoState;
StoreContext(OffsetContainer* container)
{
m_container = container;
}
Offset32Ptr<FileState> findFile(const String& uniqueIdentity)
{
Offset32Ptr<FileState> file;
m_uniqueToFileMap.tryGetValue(uniqueIdentity, file);
return file;
}
Offset32Ptr<FileState> addFile(const String& uniqueIdentity, const UnownedStringSlice* content)
{
OffsetBase& base = m_container->asBase();
Offset32Ptr<FileState> file;
// Get the file, if it has an identity
if (uniqueIdentity.getLength())
{
if (!m_uniqueToFileMap.tryGetValue(uniqueIdentity, file))
{
// If file was not found create it
// Create the file
file = m_container->newObject<FileState>();
// Add it
m_uniqueToFileMap.add(uniqueIdentity, file);
// Set the identity
auto offsetUniqueIdentity = m_container->newString(uniqueIdentity.getUnownedSlice());
base[file]->uniqueIdentity = offsetUniqueIdentity;
// Add the file
m_files.add(file);
}
}
else
{
// Create a file, but we know it can't have unique identity
file = m_container->newObject<FileState>();
// Add the file
m_files.add(file);
}
// If the contents is not set add it
if (!base[file]->contents && content)
{
auto offsetContent = m_container->newString(*content);
base[file]->contents = offsetContent;
}
return file;
}
Offset32Ptr<SourceFileState> addSourceFile(SourceFile* sourceFile)
{
if (!sourceFile)
{
return Offset32Ptr<SourceFileState>();
}
auto& base = m_container->asBase();
Offset32Ptr<ReproUtil::SourceFileState> sourceFileState;
if (m_sourceFileMap.tryGetValue(sourceFile, sourceFileState))
{
return sourceFileState;
}
const PathInfo& pathInfo = sourceFile->getPathInfo();
UnownedStringSlice content = sourceFile->getContent();
Offset32Ptr<FileState> file = addFile(pathInfo.uniqueIdentity, &content);
Offset32Ptr<OffsetString> foundPath;
if (pathInfo.foundPath.getLength() && base[file]->foundPath.isNull())
{
foundPath = fromString(pathInfo.foundPath.getUnownedSlice());
}
// Set on the file
base[file]->foundPath = foundPath;
// Create the source file
sourceFileState = m_container->newObject<SourceFileState>();
{
auto dst = base[sourceFileState];
dst->file = file;
dst->foundPath = foundPath;
dst->type = pathInfo.type;
}
m_sourceFileMap.add(sourceFile, sourceFileState);
return sourceFileState;
}
Offset32Ptr<OffsetString> fromString(const String& in)
{
Offset32Ptr<OffsetString> value;
if (m_stringMap.tryGetValue(in, value))
{
return value;
}
value = m_container->newString(in.getUnownedSlice());
m_stringMap.add(in, value);
return value;
}
Offset32Ptr<OffsetString> fromName(Name* name)
{
if (name)
{
return fromString(name->text);
}
return Offset32Ptr<OffsetString>();
}
Offset32Ptr<PathInfoState> addPathInfo(const CacheFileSystem::PathInfo* srcPathInfo)
{
if (!srcPathInfo)
{
return Offset32Ptr<PathInfoState>();
}
OffsetBase& base = m_container->asBase();
Offset32Ptr<PathInfoState> pathInfo;
if (!m_pathInfoMap.tryGetValue(srcPathInfo, pathInfo))
{
// Get the associated file
Offset32Ptr<FileState> fileState;
// Only store as file if we have the contents
if(ISlangBlob* fileBlob = srcPathInfo->m_fileBlob)
{
UnownedStringSlice content((const char*)fileBlob->getBufferPointer(), fileBlob->getBufferSize());
fileState = addFile(srcPathInfo->getUniqueIdentity(), &content);
}
// Save the rest of the state
pathInfo = m_container->newObject<PathInfoState>();
PathInfoState& dst = base[*pathInfo];
dst.file = fileState;
// Save any other info
dst.getCanonicalPathResult = srcPathInfo->m_getCanonicalPathResult;
dst.getPathTypeResult = srcPathInfo->m_getPathTypeResult;
dst.loadFileResult = srcPathInfo->m_loadFileResult;
dst.pathType = srcPathInfo->m_pathType;
m_pathInfoMap.add(srcPathInfo, pathInfo);
}
// Fill in info on the file
auto fileState(base[pathInfo]->file);
// If have fileState add any missing element
if (fileState)
{
if (srcPathInfo->m_fileBlob && base[fileState]->contents.isNull())
{
UnownedStringSlice contents((const char*)srcPathInfo->m_fileBlob->getBufferPointer(), srcPathInfo->m_fileBlob->getBufferSize());
const auto offsetContents = m_container->newString(contents);
base[fileState]->contents = offsetContents;
}
if (srcPathInfo->m_canonicalPath.getLength() && base[fileState]->canonicalPath.isNull())
{
const auto offsetCanonicalPath = fromString(srcPathInfo->m_canonicalPath);
base[fileState]->canonicalPath = offsetCanonicalPath;
}
if (srcPathInfo->m_uniqueIdentity.getLength() && base[fileState]->uniqueIdentity.isNull())
{
const auto offsetUniqueIdentity = fromString(srcPathInfo->m_uniqueIdentity);
base[fileState]->uniqueIdentity = offsetUniqueIdentity;
}
}
return pathInfo;
}
const Offset32Array<ReproUtil::StringPair> calcDefines(const Dictionary<String, String>& srcDefines)
{
typedef ReproUtil::StringPair StringPair;
Offset32Array<StringPair> dstDefines = m_container->newArray<StringPair>(srcDefines.getCount());
OffsetBase& base = m_container->asBase();
Index index = 0;
for (const auto& [srcDefineName, srcDefineVal] : srcDefines)
{
auto& dstDefine = base[dstDefines[index]];
dstDefine.first = fromString(srcDefineName);
dstDefine.second = fromString(srcDefineVal);
index++;
}
return dstDefines;
}
const Offset32Array<Offset32Ptr<OffsetString>> fromList(const List<String>& src)
{
Offset32Array<Offset32Ptr<OffsetString>> dst = m_container->newArray<Offset32Ptr<OffsetString>>(src.getCount());
OffsetBase& base = m_container->asBase();
for (Index j = 0; j < src.getCount(); ++j)
{
const auto offsetSrc = fromString(src[j]);
base[dst[j]] = offsetSrc;
}
return dst;
}
Dictionary<String, Offset32Ptr<OffsetString> > m_stringMap;
Dictionary<SourceFile*, Offset32Ptr<ReproUtil::SourceFileState> > m_sourceFileMap;
Dictionary<String, Offset32Ptr<ReproUtil::FileState> > m_uniqueToFileMap;
Dictionary<const CacheFileSystem::PathInfo*, Offset32Ptr<PathInfoState> > m_pathInfoMap;
List<Offset32Ptr<ReproUtil::FileState> > m_files;
OffsetContainer* m_container;
};
} //
static bool _isStorable(const PathInfo::Type type)
{
switch (type)
{
case PathInfo::Type::Unknown:
case PathInfo::Type::Normal:
case PathInfo::Type::FoundPath:
case PathInfo::Type::FromString:
{
return true;
}
default: return false;
}
}
static String _scrubName(const String& in)
{
StringBuilder builder;
for (auto c : in)
{
switch (c)
{
case ':': c = '_'; break;
default:break;
}
builder.appendChar(c);
}
return builder.produceString();
}
/* static */SlangResult ReproUtil::store(EndToEndCompileRequest* request, OffsetContainer& inOutContainer, Offset32Ptr<RequestState>& outRequest)
{
StoreContext context(&inOutContainer);
OffsetBase& base = inOutContainer.asBase();
auto linkage = request->getLinkage();
Offset32Ptr<RequestState> requestState = inOutContainer.newObject<RequestState>();
{
RequestState* dst = base[requestState];
dst->compileFlags = request->getFrontEndReq()->compileFlags;
dst->shouldDumpIntermediates = request->shouldDumpIntermediates;
dst->debugInfoLevel = linkage->debugInfoLevel;
dst->optimizationLevel = linkage->optimizationLevel;
dst->containerFormat = request->m_containerFormat;
dst->passThroughMode = request->m_passThrough;
dst->useUnknownImageFormatAsDefault = request->useUnknownImageFormatAsDefault;
dst->obfuscateCode = linkage->m_obfuscateCode;
dst->defaultMatrixLayoutMode = SlangMatrixLayoutMode(linkage->defaultMatrixLayoutMode);
}
// Entry points
{
const auto& srcEntryPoints = request->getFrontEndReq()->m_entryPointReqs;
const auto& srcEndToEndEntryPoints = request->m_entryPoints;
SLANG_ASSERT(srcEntryPoints.getCount() == srcEndToEndEntryPoints.getCount());
Offset32Array<EntryPointState> dstEntryPoints = inOutContainer.newArray<EntryPointState>(srcEntryPoints.getCount());
for (Index i = 0; i < srcEntryPoints.getCount(); ++i)
{
FrontEndEntryPointRequest* srcEntryPoint = srcEntryPoints[i];
const auto& srcEndToEndEntryPoint = srcEndToEndEntryPoints[i];
auto dstSpecializationArgStrings = context.fromList(srcEndToEndEntryPoint.specializationArgStrings);
Offset32Ptr<OffsetString> dstName = context.fromName(srcEntryPoint->getName());
EntryPointState& dst = base[dstEntryPoints[i]];
dst.profile = srcEntryPoint->getProfile();
dst.translationUnitIndex = uint32_t(srcEntryPoint->getTranslationUnitIndex());
dst.specializationArgStrings = dstSpecializationArgStrings;
dst.name = dstName;
}
base[requestState]->entryPoints = dstEntryPoints;
}
// Add all of the source files
{
SourceManager* sourceManager = request->getFrontEndReq()->getSourceManager();
const List<SourceFile*>& sourceFiles = sourceManager->getSourceFiles();
for (SourceFile* sourceFile : sourceFiles)
{
const PathInfo& pathInfo = sourceFile->getPathInfo();
if (_isStorable(pathInfo.type))
{
context.addSourceFile(sourceFile);
}
}
}
// Add all the target requests
{
Offset32Array<TargetRequestState> dstTargets = inOutContainer.newArray<TargetRequestState>(linkage->targets.getCount());
for (Index i = 0; i < linkage->targets.getCount(); ++i)
{
TargetRequest* srcTargetRequest = linkage->targets[i];
// Copy the simple stuff
{
auto& dst = base[dstTargets[i]];
dst.target = srcTargetRequest->getTarget();
dst.profile = srcTargetRequest->getTargetProfile();
dst.targetFlags = srcTargetRequest->getTargetFlags();
dst.floatingPointMode = srcTargetRequest->getFloatingPointMode();
}
// Copy the entry point/target output names
{
const auto& srcTargetInfos = request->m_targetInfos;
if (const RefPtr<EndToEndCompileRequest::TargetInfo>* infosPtr = srcTargetInfos.tryGetValue(srcTargetRequest))
{
EndToEndCompileRequest::TargetInfo* infos = *infosPtr;
const auto& entryPointOutputPaths = infos->entryPointOutputPaths;
Offset32Array<OutputState> dstOutputStates = inOutContainer.newArray<OutputState>(entryPointOutputPaths.getCount());
Index index = 0;
for (const auto& [key, value] : entryPointOutputPaths)
{
Offset32Ptr<OffsetString> outputPath = inOutContainer.newString(value.getUnownedSlice());
auto& dstOutputState = base[dstOutputStates[index]];
dstOutputState.entryPointIndex = int32_t(key);
dstOutputState.outputPath = outputPath;
index++;
}
base[dstTargets[i]].outputStates = dstOutputStates;
}
}
}
// Save the result
base[requestState]->targetRequests = dstTargets;
}
// Add the search paths
{
const auto& srcPaths = linkage->searchDirectories.searchDirectories;
Offset32Array<Offset32Ptr<OffsetString> > dstPaths = inOutContainer.newArray<Offset32Ptr<OffsetString> >(srcPaths.getCount());
// We don't handle parents here
SLANG_ASSERT(linkage->searchDirectories.parent == nullptr);
for (Index i = 0; i < srcPaths.getCount(); ++i)
{
const auto srcPath = context.fromString(srcPaths[i].path);
base[dstPaths[i]] = srcPath;
}
base[requestState]->searchPaths = dstPaths;
}
// Add preprocessor definitions
base[requestState]->preprocessorDefinitions = context.calcDefines(linkage->preprocessorDefinitions);
{
const auto& srcTranslationUnits = request->getFrontEndReq()->translationUnits;
Offset32Array<TranslationUnitRequestState> dstTranslationUnits = inOutContainer.newArray<TranslationUnitRequestState>(srcTranslationUnits.getCount());
for (Index i = 0; i < srcTranslationUnits.getCount(); ++i)
{
TranslationUnitRequest* srcTranslationUnit = srcTranslationUnits[i];
// Do before setting, because this can allocate, and therefore break, the following section
auto defines = context.calcDefines(srcTranslationUnit->preprocessorDefinitions);
auto moduleName = context.fromName(srcTranslationUnit->moduleName);
Offset32Array<Offset32Ptr<SourceFileState>> dstSourceFiles;
{
const auto& srcFiles = srcTranslationUnit->getSourceFiles();
dstSourceFiles = inOutContainer.newArray<Offset32Ptr<SourceFileState> >(srcFiles.getCount());
for (Index j = 0; j < srcFiles.getCount(); ++j)
{
const auto srcFile = context.addSourceFile(srcFiles[j]);
base[dstSourceFiles[j]] = srcFile;
}
}
TranslationUnitRequestState& dstTranslationUnit = base[dstTranslationUnits[i]];
dstTranslationUnit.language = srcTranslationUnit->sourceLanguage;
dstTranslationUnit.moduleName = moduleName;
dstTranslationUnit.sourceFiles = dstSourceFiles;
dstTranslationUnit.preprocessorDefinitions = defines;
}
base[requestState]->translationUnits = dstTranslationUnits;
}
// Find files from the file system, and mapping paths to files
{
CacheFileSystem* cacheFileSystem = as<CacheFileSystem>(linkage->getFileSystemExt());
if (!cacheFileSystem)
{
return SLANG_FAIL;
}
// Traverse the references (in process we will construct the map from PathInfo)
{
const auto& srcFiles = cacheFileSystem->getPathMap();
Offset32Array<PathAndPathInfo> pathMap = inOutContainer.newArray<PathAndPathInfo>(srcFiles.getCount());
Index index = 0;
for (const auto& [key, value] : srcFiles)
{
const auto path = context.fromString(key);
const auto pathInfo = context.addPathInfo(value);
PathAndPathInfo& dstInfo = base[pathMap[index]];
dstInfo.path = path;
dstInfo.pathInfo = pathInfo;
index++;
}
base[requestState]->pathInfoMap = pathMap;
}
}
// Save all of the files
{
Dictionary<String, int> uniqueNameMap;
auto files = inOutContainer.newArray<Offset32Ptr<FileState>>(context.m_files.getCount());
for (Index i = 0; i < context.m_files.getCount(); ++i)
{
Offset32Ptr<FileState> file = context.m_files[i];
// Need to come up with unique names
String path;
if (auto canonicalPath = base[file]->canonicalPath)
{
path = base[canonicalPath]->getSlice();
}
else if (auto foundPath = base[file]->foundPath)
{
path = base[foundPath]->getSlice();
}
else if (auto uniqueIdentity = base[file]->uniqueIdentity)
{
path = base[uniqueIdentity]->getSlice();
}
if (path.getLength() == 0)
{
StringBuilder builder;
builder << "unnamed" << i;
path = builder;
}
String filename = _scrubName(Path::getFileNameWithoutExt(path));
String ext = _scrubName(Path::getPathExt(path));
StringBuilder uniqueName;
for (Index j = 0; j < 0x10000; j++)
{
uniqueName.clear();
uniqueName << filename;
if (j > 0)
{
uniqueName << "-" << j;
}
if (ext.getLength())
{
uniqueName << "." << ext;
}
int dummy = 0;
if (!uniqueNameMap.tryGetValueOrAdd(uniqueName, dummy))
{
// It was added so we are done
break;
}
}
// Save the unique generated name
const auto offsetUniqueName = inOutContainer.newString(uniqueName.getUnownedSlice());
base[file]->uniqueName = offsetUniqueName;
base[files[i]] = file;
}
base[requestState]->files = files;
}
// Save all the SourceFile state
{
const auto& srcSourceFiles = context.m_sourceFileMap;
auto dstSourceFiles = inOutContainer.newArray<Offset32Ptr<SourceFileState>>(srcSourceFiles.getCount());
Index index = 0;
for (const auto& [_, value] : srcSourceFiles)
{
base[dstSourceFiles[index]] = value;
index++;
}
base[requestState]->sourceFiles = dstSourceFiles;
}
outRequest = requestState;
return SLANG_OK;
}
namespace { // anonymous
struct LoadContext
{
typedef ReproUtil::SourceFileState SourceFileState;
typedef ReproUtil::FileState FileState;
typedef ReproUtil::PathInfoState PathInfoState;
CacheFileSystem::PathInfo* getPathInfoFromFile(FileState* file)
{
if (!file)
{
return nullptr;
}
CacheFileSystem::PathInfo* dstInfo = nullptr;
if (!m_fileToPathInfoMap.tryGetValue(file, dstInfo))
{
ComPtr<ISlangBlob> blob;
if (m_fileSystem && file->uniqueName)
{
// Try loading from the file system
m_fileSystem->loadFile(m_base->asRaw(file->uniqueName)->getCstr(), blob.writeRef());
}
// If wasn't loaded, and has contents, use that
if (!blob && file->contents)
{
blob = StringBlob::create(m_base->asRaw(file->contents)->getSlice());
}
dstInfo = new CacheFileSystem::PathInfo(String());
if (file->uniqueIdentity)
{
dstInfo->m_uniqueIdentity = m_base->asRaw(file->uniqueIdentity)->getSlice();
}
if (file->canonicalPath)
{
dstInfo->m_canonicalPath = m_base->asRaw(file->canonicalPath)->getSlice();
}
if (blob)
{
dstInfo->m_loadFileResult = CacheFileSystem::CompressedResult::Ok;
dstInfo->m_getPathTypeResult = CacheFileSystem::CompressedResult::Ok;
dstInfo->m_pathType = SLANG_PATH_TYPE_FILE;
}
dstInfo->m_fileBlob = blob;
// Add to map, even if the blob is nullptr (say from a failed read)
m_fileToPathInfoMap.add(file, dstInfo);
}
return dstInfo;
}
ISlangBlob* getFileBlobFromFile(FileState* file)
{
CacheFileSystem::PathInfo* pathInfo = getPathInfoFromFile(file);
return pathInfo ? pathInfo->m_fileBlob.get() : nullptr;
}
SourceFile* getSourceFile(SourceFileState* sourceFile)
{
if (sourceFile == nullptr)
{
return nullptr;
}
SourceFile* dstFile;
if (!m_sourceFileMap.tryGetValue(sourceFile, dstFile))
{
FileState* file = m_base->asRaw(sourceFile->file);
ISlangBlob* blob = getFileBlobFromFile(file);
PathInfo pathInfo;
pathInfo.type = sourceFile->type;
if (sourceFile->foundPath)
{
pathInfo.foundPath = m_base->asRaw(sourceFile->foundPath)->getSlice();
}
else if (file->foundPath)
{
pathInfo.foundPath = m_base->asRaw(file->foundPath)->getSlice();
}
if (file->uniqueIdentity)
{
pathInfo.uniqueIdentity = m_base->asRaw(file->uniqueIdentity)->getSlice();
}
dstFile = new SourceFile(m_sourceManager, pathInfo, blob->getBufferSize());
dstFile->setContents(blob);
// Add to map
m_sourceFileMap.add(sourceFile, dstFile);
// Add to manager
m_sourceManager->addSourceFile(pathInfo.uniqueIdentity, dstFile);
}
return dstFile;
}
CacheFileSystem::PathInfo* addPathInfo(const PathInfoState* srcInfo)
{
if (!srcInfo)
{
return nullptr;
}
CacheFileSystem::PathInfo* pathInfo;
if (m_pathInfoMap.tryGetValue(srcInfo, pathInfo))
{
return pathInfo;
}
FileState* file = m_base->asRaw(srcInfo->file);
CacheFileSystem::PathInfo* dstInfo;
if (file)
{
dstInfo = getPathInfoFromFile(file);
}
else
{
// TODO(JS): Hmmm... this could end up not being cleared up
// Because it is not added to the unique set (as unique set is for files and this isn't a file)
dstInfo = new CacheFileSystem::PathInfo(String());
}
dstInfo->m_getCanonicalPathResult = srcInfo->getCanonicalPathResult;
dstInfo->m_getPathTypeResult = srcInfo->getPathTypeResult;
dstInfo->m_loadFileResult = srcInfo->loadFileResult;
dstInfo->m_pathType = srcInfo->pathType;
m_pathInfoMap.add(srcInfo, dstInfo);
return dstInfo;
}
List<const char*> toList(const Offset32Array<Offset32Ptr<OffsetString>>& src)
{
List<const char*> dst;
dst.setCount(src.getCount());
for (Index i = 0; i < src.getCount(); ++i)
{
OffsetString* srcString = m_base->asRaw(m_base->asRaw(src[i]));
dst[i] = srcString ? srcString->getCstr() : nullptr;
}
return dst;
}
void loadDefines(const Offset32Array<ReproUtil::StringPair>& in, Dictionary<String, String>& out)
{
out.clear();
for (const auto& define : in)
{
out.add(m_base->asRaw(m_base->asRaw(define).first)->getSlice(), m_base->asRaw(m_base->asRaw(define).second)->getSlice());
}
}
LoadContext(SourceManager* sourceManger, ISlangFileSystem* fileSystem, OffsetBase* base):
m_sourceManager(sourceManger),
m_fileSystem(fileSystem),
m_base(base)
{
}
ISlangFileSystem* m_fileSystem;
OffsetBase* m_base;
SourceManager* m_sourceManager;
Dictionary<SourceFileState*, SourceFile*> m_sourceFileMap;
Dictionary<FileState*, CacheFileSystem::PathInfo*> m_fileToPathInfoMap;
Dictionary<const PathInfoState*, CacheFileSystem::PathInfo*> m_pathInfoMap;
};
} // anonymous
/* static */SlangResult ReproUtil::loadFileSystem(OffsetBase& base, RequestState* requestState, ISlangFileSystem* replaceFileSystem, ComPtr<ISlangFileSystemExt>& outFileSystem)
{
LoadContext context(nullptr, replaceFileSystem, &base);
CacheFileSystem* cacheFileSystem = new CacheFileSystem(nullptr);
ComPtr<ISlangFileSystemExt> scopeCacheFileSystem(cacheFileSystem);
auto& dstUniqueMap = cacheFileSystem->getUniqueMap();
auto& dstPathMap = cacheFileSystem->getPathMap();
for (auto fileOffset : requestState->files)
{
// add the file
FileState* fileState = base.asRaw(base.asRaw(fileOffset));
CacheFileSystem::PathInfo* pathInfo = context.getPathInfoFromFile(fileState);
if (fileState->foundPath)
{
String foundPath = base.asRaw(fileState->foundPath)->getSlice();
dstPathMap.addIfNotExists(foundPath, pathInfo);
}
}
// Put all the paths to path info
{
for (const auto& pairOffset : requestState->pathInfoMap)
{
const auto& pair = base.asRaw(pairOffset);
CacheFileSystem::PathInfo* pathInfo = context.addPathInfo(base.asRaw(pair.pathInfo));
dstPathMap.addIfNotExists(base.asRaw(pair.path)->getSlice(), pathInfo);
}
}
// Put all the path infos in the cache system
{
for (const auto& [_, pathInfo] : context.m_fileToPathInfoMap)
{
SLANG_ASSERT(pathInfo->m_uniqueIdentity.getLength());
dstUniqueMap.add(pathInfo->m_uniqueIdentity, pathInfo);
// Add canonical paths too..
if (pathInfo->m_canonicalPath.getLength())
{
String canonicalPath = pathInfo->m_canonicalPath;
dstPathMap.addIfNotExists(canonicalPath, pathInfo);
}
}
}
outFileSystem.swap(scopeCacheFileSystem);
return SLANG_OK;
}
/* static */SlangResult ReproUtil::load(OffsetBase& base, RequestState* requestState, ISlangFileSystem* optionalFileSystem, EndToEndCompileRequest* request)
{
auto externalRequest = asExternal(request);
auto linkage = request->getLinkage();
// TODO(JS): Really should be more exhaustive here, and set up to initial state ideally
// Reset state
{
request->m_targetInfos.clear();
// Remove any requests
linkage->targets.clear();
}
LoadContext context(linkage->getSourceManager(), optionalFileSystem, &base);
// Try to set state through API - as doing so means if state stored in multiple places it will be ok
{
externalRequest->setCompileFlags((SlangCompileFlags)requestState->compileFlags);
externalRequest->setDumpIntermediates(int(requestState->shouldDumpIntermediates));
externalRequest->setLineDirectiveMode(SlangLineDirectiveMode(requestState->lineDirectiveMode));
externalRequest->setDebugInfoLevel(SlangDebugInfoLevel(requestState->debugInfoLevel));
externalRequest->setOptimizationLevel(SlangOptimizationLevel(requestState->optimizationLevel));
externalRequest->setOutputContainerFormat(SlangContainerFormat(requestState->containerFormat));
externalRequest->setPassThrough(SlangPassThrough(request->m_passThrough));
request->useUnknownImageFormatAsDefault = requestState->useUnknownImageFormatAsDefault;
linkage->m_obfuscateCode = requestState->obfuscateCode;
linkage->setMatrixLayoutMode(requestState->defaultMatrixLayoutMode);
}
// Add the target requests
{
for (Index i = 0; i < requestState->targetRequests.getCount(); ++i)
{
TargetRequestState& src = base.asRaw(requestState->targetRequests[i]);
int index = externalRequest->addCodeGenTarget(SlangCompileTarget(src.target));
SLANG_ASSERT(index == i);
auto dstTarget = linkage->targets[index];
SLANG_ASSERT(dstTarget->getTarget() == src.target);
dstTarget->setTargetProfile(src.profile);
dstTarget->addTargetFlags(src.targetFlags);
dstTarget->setFloatingPointMode(src.floatingPointMode);
// If there is output state (like output filenames) add here
if (src.outputStates.getCount())
{
RefPtr<EndToEndCompileRequest::TargetInfo> dstTargetInfo(new EndToEndCompileRequest::TargetInfo);
request->m_targetInfos[dstTarget] = dstTargetInfo;
for (const auto& srcOutputStateOffset : src.outputStates)
{
const auto& srcOutputState = base.asRaw(srcOutputStateOffset);
SLANG_ASSERT(srcOutputState.entryPointIndex < requestState->entryPoints.getCount());
String entryPointPath;
if (srcOutputState.outputPath)
{
entryPointPath = base.asRaw(srcOutputState.outputPath)->getSlice();
}
dstTargetInfo->entryPointOutputPaths.add(srcOutputState.entryPointIndex, entryPointPath);
}
}
}
}
{
const auto& srcPaths = requestState->searchPaths;
auto& dstPaths = linkage->searchDirectories.searchDirectories;
dstPaths.setCount(srcPaths.getCount());
for (Index i = 0; i < srcPaths.getCount(); ++i)
{
dstPaths[i].path = base.asRaw(base.asRaw(srcPaths[i]))->getSlice();
}
}
context.loadDefines(requestState->preprocessorDefinitions, linkage->preprocessorDefinitions);
{
auto frontEndReq = request->getFrontEndReq();
const auto& srcTranslationUnits = requestState->translationUnits;
auto& dstTranslationUnits = frontEndReq->translationUnits;
dstTranslationUnits.clear();
for (Index i = 0; i < srcTranslationUnits.getCount(); ++i)
{
const auto& srcTranslationUnit = base.asRaw(srcTranslationUnits[i]);
// TODO(JS): We should probably serialize off the module name
// Passing in nullptr will just generate the module name
int index = frontEndReq->addTranslationUnit(srcTranslationUnit.language, nullptr);
SLANG_UNUSED(index);
SLANG_ASSERT(index == i);
TranslationUnitRequest* dstTranslationUnit = dstTranslationUnits[i];
context.loadDefines(srcTranslationUnit.preprocessorDefinitions, dstTranslationUnit->preprocessorDefinitions);
Name* moduleName = nullptr;
if (srcTranslationUnit.moduleName)
{
moduleName = request->getNamePool()->getName(base.asRaw(srcTranslationUnit.moduleName)->getSlice());
}
dstTranslationUnit->moduleName = moduleName;
const auto& srcSourceFiles = srcTranslationUnit.sourceFiles;
dstTranslationUnit->clearSource();
const auto sourceDesc = ArtifactDescUtil::makeDescForSourceLanguage(asExternal(dstTranslationUnit->sourceLanguage));
for (Index j = 0; j < srcSourceFiles.getCount(); ++j)
{
// Create the source file
SourceFile* sourceFile = context.getSourceFile(base.asRaw(base.asRaw(srcSourceFiles[i])));
// Create the artifact
auto sourceArtifact = ArtifactUtil::createArtifact(sourceDesc, sourceFile->getPathInfo().getName().getBuffer());
if (sourceFile->getContentBlob())
{
sourceArtifact->addRepresentationUnknown(sourceFile->getContentBlob());
}
// Add to translation unit
dstTranslationUnit->addSource(sourceArtifact, sourceFile);
}
}
}
// Entry points
{
// Check there aren't any set entry point
SLANG_ASSERT(request->getFrontEndReq()->m_entryPointReqs.getCount() == 0);
for (const auto& srcEntryPointOffset : requestState->entryPoints)
{
const auto srcEntryPoint = base.asRaw(srcEntryPointOffset);
const char* name = srcEntryPoint.name ? base.asRaw(srcEntryPoint.name)->getCstr() : nullptr;
Stage stage = srcEntryPoint.profile.getStage();
List<const char*> args = context.toList(srcEntryPoint.specializationArgStrings);
externalRequest->addEntryPointEx(int(srcEntryPoint.translationUnitIndex), name, SlangStage(stage), int(args.getCount()), args.getBuffer());
}
}
{
auto cacheFileSystem = new CacheFileSystem(request->m_reproFallbackFileSystem);
ComPtr<ISlangFileSystemExt> fileSystemExt(cacheFileSystem);
auto& dstUniqueMap = cacheFileSystem->getUniqueMap();
auto& dstPathMap = cacheFileSystem->getPathMap();
// Put all the paths to path info
{
for (const auto& pairOffset : requestState->pathInfoMap)
{
const auto& pair = base.asRaw(pairOffset);
auto srcPathInfo = base.asRaw(pair.pathInfo);
CacheFileSystem::PathInfo* pathInfo = context.addPathInfo(srcPathInfo);
dstPathMap.add(base.asRaw(pair.path)->getSlice(), pathInfo);
}
}
// Put all the path infos in the cache system
{
for (const auto& [_, pathInfo] : context.m_fileToPathInfoMap)
{
// TODO(JS): It's not 100% clear why we are ending up
// with entries that don't have a unique identity.
// For now we ignore adding to the unique map, because
// if we do we'll have multiple entries with the same key
if (pathInfo->m_uniqueIdentity.getLength() == 0)
{
continue;
}
SLANG_ASSERT(pathInfo->m_uniqueIdentity.getLength());
dstUniqueMap.add(pathInfo->m_uniqueIdentity, pathInfo);
}
}
// This is a bit of a hack, we are going to replace the file system, with our one which is filled in
// with what was read from the file.
linkage->m_fileSystemExt.swap(fileSystemExt);
}
return SLANG_OK;
}
/* static */SlangResult ReproUtil::saveState(EndToEndCompileRequest* request, Stream* stream)
{
OffsetContainer container;
Offset32Ptr<RequestState> requestState;
SLANG_RETURN_ON_FAIL(store(request, container, requestState));
Header header;
header.m_chunk.type = kSlangStateFourCC;
header.m_semanticVersion = g_semanticVersion;
header.m_typeHash = _getTypeHash();
return RiffUtil::writeData(&header.m_chunk, sizeof(header),container.getData(), container.getDataCount(), stream);
}
/* static */SlangResult ReproUtil::saveState(EndToEndCompileRequest* request, const String& filename)
{
RefPtr<FileStream> stream(new FileStream);
SLANG_RETURN_ON_FAIL(stream->init(filename, FileMode::Create, FileAccess::Write, FileShare::ReadWrite));
return saveState(request, stream);
}
/* static */ SlangResult ReproUtil::loadState(const String& filename, DiagnosticSink* sink, List<uint8_t>& outBuffer)
{
RefPtr<FileStream> stream = new FileStream;
SLANG_RETURN_ON_FAIL(stream->init(filename, FileMode::Open, FileAccess::Read, FileShare::ReadWrite));
return loadState(stream, sink, outBuffer);
}
/* static */ SlangResult ReproUtil::loadState(Stream* stream, DiagnosticSink* sink, List<uint8_t>& buffer)
{
Header header;
{
Result res = RiffUtil::readData(stream, &header.m_chunk, sizeof(header), buffer);
if (SLANG_FAILED(res))
{
sink->diagnose(SourceLoc(), Diagnostics::unableToReadRiff);
return res;
}
}
if (header.m_chunk.type != kSlangStateFourCC)
{
sink->diagnose(SourceLoc(), Diagnostics::expectingSlangRiffContainer);
return SLANG_FAIL;
}
if (!RiffSemanticVersion::areCompatible(g_semanticVersion, header.m_semanticVersion))
{
StringBuilder headerBuf, currentBuf;
header.m_semanticVersion.asSemanticVersion().append(headerBuf);
g_semanticVersion.asSemanticVersion().append(currentBuf);
sink->diagnose(SourceLoc(), Diagnostics::incompatibleRiffSemanticVersion, headerBuf, currentBuf);
return SLANG_FAIL;
}
if (header.m_typeHash != _getTypeHash())
{
sink->diagnose(SourceLoc(), Diagnostics::riffHashMismatch);
return SLANG_FAIL;
}
return SLANG_OK;
}
/* static */SlangResult ReproUtil::loadState(const uint8_t* data, size_t size, DiagnosticSink* sink, List<uint8_t>& outBuffer)
{
MemoryStreamBase stream(FileAccess::Read, data, size);
return loadState(&stream, sink, outBuffer);
}
/* static */ ReproUtil::RequestState* ReproUtil::getRequest(const List<uint8_t>& buffer)
{
return (ReproUtil::RequestState*)(buffer.getBuffer() + kStartOffset);
}
/* static */SlangResult ReproUtil::calcDirectoryPathFromFilename(const String& filename, String& outPath)
{
String absPath;
SLANG_RETURN_ON_FAIL(Path::getCanonical(filename, absPath));
String parentDir = Path::getParentDirectory(absPath);
String baseName = Path::getFileNameWithoutExt(filename);
String ext = Path::getPathExt(filename);
if (ext.getLength() == 0)
{
StringBuilder builder;
builder << baseName << "-files";
baseName = builder;
}
outPath = Path::combine(parentDir, baseName);
return SLANG_OK;
}
/* static */SlangResult ReproUtil::extractFilesToDirectory(const String& filename, DiagnosticSink* sink)
{
List<uint8_t> buffer;
SLANG_RETURN_ON_FAIL(ReproUtil::loadState(filename, sink, buffer));
MemoryOffsetBase base;
base.set(buffer.getBuffer(), buffer.getCount());
RequestState* requestState = ReproUtil::getRequest(buffer);
String dirPath;
SLANG_RETURN_ON_FAIL(ReproUtil::calcDirectoryPathFromFilename(filename, dirPath));
if (!Path::createDirectory(dirPath))
{
sink->diagnose(SourceLoc(), Diagnostics::unableToCreateDirectory, dirPath);
return SLANG_FAIL;
}
// Set up a file system to write into this directory
RelativeFileSystem relFileSystem(OSFileSystem::getMutableSingleton(), dirPath);
return extractFiles(base, requestState, &relFileSystem);
}
static void _calcPreprocessorDefines(OffsetBase& base, const Offset32Array<ReproUtil::StringPair>& srcDefines, CommandLine& cmd)
{
for (const auto& define : srcDefines)
{
StringBuilder builder;
builder << "-D" << base.asRaw(base.asRaw(define).first)->getSlice();
if (base.asRaw(define).second)
{
builder << "=" << base.asRaw(base.asRaw(define).second)->getSlice();
}
cmd.addArg(builder);
}
}
static SlangResult _calcCommandLine(OffsetBase& base, ReproUtil::RequestState* requestState, CommandLine& cmd)
{
typedef ReproUtil::TargetRequestState TargetRequestState;
typedef ReproUtil::SourceFileState SourceFileState;
{
SlangCompileFlags flags = (SlangCompileFlags)requestState->compileFlags;
while (flags)
{
// Extract a bit
const SlangCompileFlags isolatedBit = flags & SlangCompileFlags(-int(flags));
switch (isolatedBit)
{
case SLANG_COMPILE_FLAG_NO_MANGLING: cmd.addArg("-no-mangle"); break;
case SLANG_COMPILE_FLAG_NO_CODEGEN: cmd.addArg("-no-codegen"); break;
default: break;
}
// Remove the bit
flags &= ~isolatedBit;
}
//spSetDumpIntermediates(externalRequest, int(requestState->shouldDumpIntermediates));
switch (SlangLineDirectiveMode(requestState->lineDirectiveMode))
{
case SLANG_LINE_DIRECTIVE_MODE_DEFAULT: break;
case SLANG_LINE_DIRECTIVE_MODE_NONE:
{
cmd.addArg("-line-directive-mode");
cmd.addArg("none");
break;
}
default: break;
}
switch (SlangDebugInfoLevel(requestState->debugInfoLevel))
{
case SLANG_DEBUG_INFO_LEVEL_STANDARD: cmd.addArg("-g"); break;
case SLANG_DEBUG_INFO_LEVEL_NONE: cmd.addArg("-g0"); break;
case SLANG_DEBUG_INFO_LEVEL_MINIMAL: cmd.addArg("-g1"); break;
case SLANG_DEBUG_INFO_LEVEL_MAXIMAL: cmd.addArg("-g3"); break;
default: break;
}
switch (SlangOptimizationLevel(requestState->optimizationLevel))
{
case SLANG_OPTIMIZATION_LEVEL_NONE: cmd.addArg("-O0"); break;
case SLANG_OPTIMIZATION_LEVEL_DEFAULT: cmd.addArg("-O"); break;
case SLANG_OPTIMIZATION_LEVEL_HIGH: cmd.addArg("-O2"); break;
case SLANG_OPTIMIZATION_LEVEL_MAXIMAL: cmd.addArg("-O3"); break;
default: break;
}
//spSetOutputContainerFormat(externalRequest, SlangContainerFormat(requestState->containerFormat));
switch (SlangPassThrough(requestState->passThroughMode))
{
case SLANG_PASS_THROUGH_NONE: break;
default:
{
cmd.addArg("-pass-through");
cmd.addArg(TypeTextUtil::getPassThroughName(SlangPassThrough(requestState->passThroughMode)));
break;
}
}
//request->getBackEndReq()->useUnknownImageFormatAsDefault = requestState->useUnknownImageFormatAsDefault;
//request->getBackEndReq()->obfuscateCode = requestState->obfuscateCode;
//request->getFrontEndReq()->obfuscateCode = requestState->obfuscateCode;
switch (requestState->defaultMatrixLayoutMode)
{
case SLANG_MATRIX_LAYOUT_ROW_MAJOR: cmd.addArg("-matrix-layout-row-major"); break;
case SLANG_MATRIX_LAYOUT_COLUMN_MAJOR: cmd.addArg("-matrix-layout-column-major"); break;
default: break;
}
}
// Add the target requests
{
for (Index i = 0; i < requestState->targetRequests.getCount(); ++i)
{
TargetRequestState& src = base.asRaw(requestState->targetRequests[i]);
cmd.addArg("-target");
cmd.addArg(TypeTextUtil::getCompileTargetName(SlangCompileTarget(src.target)));
if (src.profile != Profile::Unknown)
{
cmd.addArg("-profile");
cmd.addArg(Profile(src.profile).getName());
}
if (src.targetFlags & SLANG_TARGET_FLAG_PARAMETER_BLOCKS_USE_REGISTER_SPACES)
{
cmd.addArg("-parameter-blocks-use-register-spaces");
}
switch (src.floatingPointMode)
{
case FloatingPointMode::Fast:
{
cmd.addArg("-fp-mode");
cmd.addArg("fast");
break;
}
case FloatingPointMode::Precise:
{
cmd.addArg("-fp-mode");
cmd.addArg("precise");
break;
}
default: break;
}
#if 0
// If there is output state (like output filenames) add here
if (src.outputStates.getCount())
{
RefPtr<EndToEndCompileRequest::TargetInfo> dstTargetInfo(new EndToEndCompileRequest::TargetInfo);
request->targetInfos[dstTarget] = dstTargetInfo;
for (const auto& srcOutputStateOffset : src.outputStates)
{
const auto& srcOutputState = base.asRaw(srcOutputStateOffset);
SLANG_ASSERT(srcOutputState.entryPointIndex < requestState->entryPoints.getCount());
String entryPointPath;
if (srcOutputState.outputPath)
{
entryPointPath = base.asRaw(srcOutputState.outputPath)->getSlice();
}
dstTargetInfo->entryPointOutputPaths.add(srcOutputState.entryPointIndex, entryPointPath);
}
}
#endif
}
}
{
const auto& srcPaths = requestState->searchPaths;
for (Index i = 0; i < srcPaths.getCount(); ++i)
{
cmd.addArg("-I");
cmd.addArg(base.asRaw(base.asRaw(srcPaths[i]))->getSlice());
}
}
_calcPreprocessorDefines(base, requestState->preprocessorDefinitions, cmd);
{
const auto& srcTranslationUnits = requestState->translationUnits;
for (Index i = 0; i < srcTranslationUnits.getCount(); ++i)
{
const auto& srcTranslationUnit = base.asRaw(srcTranslationUnits[i]);
_calcPreprocessorDefines(base, srcTranslationUnit.preprocessorDefinitions, cmd);
#if 0
if (srcTranslationUnit.moduleName)
{
moduleName = base[srcTranslationUnit].moduleName->getSlice());
}
#endif
const auto& srcSourceFiles = srcTranslationUnit.sourceFiles;
for (Index j = 0; j < srcSourceFiles.getCount(); ++j)
{
SourceFileState* sourceFile = base.asRaw(base.asRaw(srcSourceFiles[i]));
OffsetString* path = base[sourceFile->foundPath];
if (path)
{
cmd.addArg(path->getSlice());
}
}
}
}
// Entry points
{
for (const auto& srcEntryPointOffset : requestState->entryPoints)
{
const auto srcEntryPoint = base.asRaw(srcEntryPointOffset);
const char* name = srcEntryPoint.name ? base.asRaw(srcEntryPoint.name)->getCstr() : nullptr;
cmd.addArg("-entry");
cmd.addArg(name);
cmd.addArg("-stage");
UnownedStringSlice stageText = getStageText(srcEntryPoint.profile.getStage());
cmd.addArg(stageText);
//cmd.addArg("-profile");
//cmd.addArg(Profile(srcEntryPoint.profile).getName());
//List<const char*> args = context.toList(srcEntryPoint.specializationArgStrings);
//externalRequest->addEntryPointEx(int(srcEntryPoint.translationUnitIndex), name, SlangStage(stage), int(args.getCount()), args.getBuffer());
}
}
return SLANG_OK;
}
/* static */SlangResult ReproUtil::extractFiles(OffsetBase& base, RequestState* requestState, ISlangMutableFileSystem* fileSystem)
{
StringBuilder builder;
builder << "[command-line]\n";
{
CommandLine cmdLine;
_calcCommandLine(base, requestState, cmdLine);
String text = cmdLine.toString();
builder << text << "\n";
}
builder << "[files]\n";
for (auto fileOffset : requestState->files)
{
auto file = base.asRaw(base.asRaw(fileOffset));
if (file->contents)
{
UnownedStringSlice contents = base.asRaw(file->contents)->getSlice();
SLANG_RETURN_ON_FAIL(fileSystem->saveFile(base.asRaw(file->uniqueName)->getCstr(), contents.begin(), contents.getLength()));
OffsetString* originalName = nullptr;
if (file->canonicalPath)
{
originalName = base.asRaw(file->canonicalPath);
}
else if (file->foundPath)
{
originalName = base.asRaw(file->foundPath);
}
else if (file->uniqueIdentity)
{
originalName = base.asRaw(file->uniqueIdentity);
}
builder << base.asRaw(file->uniqueName)->getSlice() << " -> ";
if (originalName)
{
builder << originalName->getSlice();
}
if (builder.getLength() == 0)
{
builder << "?";
}
builder << "\n";
}
}
builder << "[paths]\n";
for (const auto pathOffset : requestState->pathInfoMap)
{
const auto& path = base.asRaw(pathOffset);
builder << base.asRaw(path.path)->getSlice() << " -> ";
const auto pathInfo = base.asRaw(path.pathInfo);
if (pathInfo)
{
if (pathInfo->file)
{
builder << base.asRaw(base.asRaw(pathInfo->file)->uniqueName)->getSlice();
}
else
{
typedef CacheFileSystem::CompressedResult CompressedResult;
if (pathInfo->getPathTypeResult == CompressedResult::Ok)
{
switch (pathInfo->pathType)
{
case SLANG_PATH_TYPE_FILE: builder << "file "; break;
case SLANG_PATH_TYPE_DIRECTORY: builder << "directory "; break;
default: builder << "?"; break;
}
}
CompressedResult curRes = pathInfo->getCanonicalPathResult;
CompressedResult results[] =
{
pathInfo->getPathTypeResult,
pathInfo->loadFileResult,
};
for (auto compRes : results)
{
if (int(compRes) > int(curRes))
{
curRes = compRes;
}
}
switch (curRes)
{
default:
case CompressedResult::Uninitialized: break;
case CompressedResult::Ok: break;
case CompressedResult::NotFound: builder << "[not found]"; break;
case CompressedResult::CannotOpen: builder << "[cannot open]"; break;
case CompressedResult::Fail: builder << "[fail]"; break;
}
}
}
else
{
builder << "[not loaded]";
}
builder << "\n";
}
SLANG_RETURN_ON_FAIL(fileSystem->saveFile("manifest.txt", builder.getBuffer(), builder.getLength()));
return SLANG_OK;
}
static SlangResult _findFirstSourcePath(EndToEndCompileRequest* request, String& outFilename)
{
// We are going to look through all of the srcTranlationUnits, looking for the first filename
auto frontEndReq = request->getFrontEndReq();
const auto& srcTranslationUnits = frontEndReq->translationUnits;
for (Index i = 0; i < srcTranslationUnits.getCount(); ++i)
{
TranslationUnitRequest* srcTranslationUnit = srcTranslationUnits[i];
const auto& srcSourceFiles = srcTranslationUnit->getSourceFiles();
for (Index j = 0; j < srcSourceFiles.getCount(); ++j)
{
SourceFile* sourceFile = srcSourceFiles[j];
const PathInfo& pathInfo = sourceFile->getPathInfo();
if (pathInfo.foundPath.getLength())
{
outFilename = pathInfo.foundPath;
return SLANG_OK;
}
}
}
return SLANG_FAIL;
}
/* static */SlangResult ReproUtil::findUniqueReproDumpStream(EndToEndCompileRequest* request, String& outFileName, RefPtr<Stream>& outStream)
{
String sourcePath;
if (SLANG_FAILED(_findFirstSourcePath(request, sourcePath)))
{
sourcePath = "unknown.slang";
}
String sourceFileName = Path::getFileName(sourcePath);
String sourceBaseName = Path::getFileNameWithoutExt(sourceFileName);
RefPtr<FileStream> stream = new FileStream;
// Okay we need a unique number to make sure the name is unique
const int maxTries = 100;
for (int triesCount = 0; triesCount < maxTries; ++triesCount)
{
// We could include the count in some way perhaps, but for now let's just go with ticks
auto tick = Process::getClockTick();
StringBuilder builder;
builder << sourceBaseName << "-" << tick << ".slang-repro";
// We write out the file name tried even if it fails, as might be useful in reporting
outFileName = builder;
// We could have clashes, as we use ticks, we should get to a point where the clashes stop
if (SLANG_SUCCEEDED(stream->init(builder, FileMode::CreateNew, FileAccess::Write, FileShare::WriteOnly)))
{
outStream = stream;
return SLANG_OK;
}
// TODO(JS):
// Might make sense to sleep here - but don't seem to have cross platform func for that yet.
}
return SLANG_FAIL;
}
} // namespace Slang