#include "clang/Basic/FileManager.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/System/Path.h"
#include "llvm/Support/Streams.h"
#include "llvm/Config/config.h"
using namespace clang;
#include <sys/stat.h>
#if defined(_MSC_VER)
#define S_ISDIR(s) (_S_IFDIR & s)
#endif
#define NON_EXISTENT_DIR reinterpret_cast<DirectoryEntry*>((intptr_t)-1)
#ifdef LLVM_ON_WIN32
#define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/' || (x) == '\\')
namespace {
static std::string GetFullPath(const char *relPath)
{
char *absPathStrPtr = _fullpath(NULL, relPath, 0);
assert(absPathStrPtr && "_fullpath() returned NULL!");
std::string absPath(absPathStrPtr);
free(absPathStrPtr);
return absPath;
}
}
class FileManager::UniqueDirContainer {
llvm::StringMap<DirectoryEntry> UniqueDirs;
public:
DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) {
std::string FullPath(GetFullPath(Name));
return UniqueDirs.GetOrCreateValue(
FullPath.c_str(),
FullPath.c_str() + FullPath.size()
).getValue();
}
size_t size() { return UniqueDirs.size(); }
};
class FileManager::UniqueFileContainer {
llvm::StringMap<FileEntry, llvm::BumpPtrAllocator> UniqueFiles;
public:
FileEntry &getFile(const char *Name, struct stat &StatBuf) {
std::string FullPath(GetFullPath(Name));
return UniqueFiles.GetOrCreateValue(
FullPath.c_str(),
FullPath.c_str() + FullPath.size()
).getValue();
}
size_t size() { return UniqueFiles.size(); }
};
#else
#define IS_DIR_SEPARATOR_CHAR(x) ((x) == '/')
class FileManager::UniqueDirContainer {
std::map<std::pair<dev_t, ino_t>, DirectoryEntry> UniqueDirs;
public:
DirectoryEntry &getDirectory(const char *Name, struct stat &StatBuf) {
return UniqueDirs[std::make_pair(StatBuf.st_dev, StatBuf.st_ino)];
}
size_t size() { return UniqueDirs.size(); }
};
class FileManager::UniqueFileContainer {
std::set<FileEntry> UniqueFiles;
public:
FileEntry &getFile(const char *Name, struct stat &StatBuf) {
return
const_cast<FileEntry&>(
*UniqueFiles.insert(FileEntry(StatBuf.st_dev,
StatBuf.st_ino,
StatBuf.st_mode)).first);
}
size_t size() { return UniqueFiles.size(); }
};
#endif
FileManager::FileManager()
: UniqueDirs(*new UniqueDirContainer),
UniqueFiles(*new UniqueFileContainer),
DirEntries(64), FileEntries(64), NextFileUID(0) {
NumDirLookups = NumFileLookups = 0;
NumDirCacheMisses = NumFileCacheMisses = 0;
}
FileManager::~FileManager() {
delete &UniqueDirs;
delete &UniqueFiles;
}
const DirectoryEntry *FileManager::getDirectory(const char *NameStart,
const char *NameEnd) {
++NumDirLookups;
llvm::StringMapEntry<DirectoryEntry *> &NamedDirEnt =
DirEntries.GetOrCreateValue(NameStart, NameEnd);
if (NamedDirEnt.getValue())
return NamedDirEnt.getValue() == NON_EXISTENT_DIR
? 0 : NamedDirEnt.getValue();
++NumDirCacheMisses;
NamedDirEnt.setValue(NON_EXISTENT_DIR);
const char *InterndDirName = NamedDirEnt.getKeyData();
struct stat StatBuf;
if (stat_cached(InterndDirName, &StatBuf) || !S_ISDIR(StatBuf.st_mode)) return 0;
DirectoryEntry &UDE = UniqueDirs.getDirectory(InterndDirName, StatBuf);
NamedDirEnt.setValue(&UDE);
if (UDE.getName()) return &UDE;
UDE.Name = InterndDirName;
return &UDE;
}
#define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
const FileEntry *FileManager::getFile(const char *NameStart,
const char *NameEnd) {
++NumFileLookups;
llvm::StringMapEntry<FileEntry *> &NamedFileEnt =
FileEntries.GetOrCreateValue(NameStart, NameEnd);
if (NamedFileEnt.getValue())
return NamedFileEnt.getValue() == NON_EXISTENT_FILE
? 0 : NamedFileEnt.getValue();
++NumFileCacheMisses;
NamedFileEnt.setValue(NON_EXISTENT_FILE);
const char *SlashPos = NameEnd-1;
while (SlashPos >= NameStart && !IS_DIR_SEPARATOR_CHAR(SlashPos[0]))
--SlashPos;
const DirectoryEntry *DirInfo;
if (SlashPos < NameStart) {
const char *Name = ".";
DirInfo = getDirectory(Name, Name+1);
} else if (SlashPos == NameEnd-1)
return 0; else
DirInfo = getDirectory(NameStart, SlashPos);
if (DirInfo == 0) return 0;
const char *InterndFileName = NamedFileEnt.getKeyData();
struct stat StatBuf;
if (stat_cached(InterndFileName, &StatBuf) || S_ISDIR(StatBuf.st_mode)) { return 0;
}
FileEntry &UFE = UniqueFiles.getFile(InterndFileName, StatBuf);
NamedFileEnt.setValue(&UFE);
if (UFE.getName()) return &UFE;
UFE.Name = InterndFileName;
UFE.Size = StatBuf.st_size;
UFE.ModTime = StatBuf.st_mtime;
UFE.Dir = DirInfo;
UFE.UID = NextFileUID++;
return &UFE;
}
void FileManager::PrintStats() const {
llvm::cerr << "\n*** File Manager Stats:\n";
llvm::cerr << UniqueFiles.size() << " files found, "
<< UniqueDirs.size() << " dirs found.\n";
llvm::cerr << NumDirLookups << " dir lookups, "
<< NumDirCacheMisses << " dir cache misses.\n";
llvm::cerr << NumFileLookups << " file lookups, "
<< NumFileCacheMisses << " file cache misses.\n";
}
int MemorizeStatCalls::stat(const char *path, struct stat *buf) {
int result = ::stat(path, buf);
if (result != 0) {
struct stat empty;
StatCalls[path] = StatResult(result, empty);
}
else if (!S_ISDIR(buf->st_mode) || llvm::sys::Path(path).isAbsolute()) {
StatCalls[path] = StatResult(result, *buf);
}
return result;
}