[llvm][vfs] Abstract in-memory node creation

The creation of in-memory VFS nodes happens in a single function that deduces what kind of node to create from the arguments. This leads to complicated if-then-else logic that's difficult to cleanly extend.

This patch abstracts away in-memory node creation via a type-erased factory function that's passed instead.

Reviewed By: dexonsmith

Differential Revision: https://reviews.llvm.org/D117648
This commit is contained in:
Jan Svoboda 2022-01-20 15:07:39 +01:00
parent 9e24d14ac8
commit 9011903e36
2 changed files with 58 additions and 31 deletions

View File

@ -419,6 +419,21 @@ namespace detail {
class InMemoryDirectory;
class InMemoryFile;
class InMemoryNode;
struct NewInMemoryNodeInfo {
llvm::sys::fs::UniqueID DirUID;
StringRef Path;
StringRef Name;
time_t ModificationTime;
std::unique_ptr<llvm::MemoryBuffer> Buffer;
uint32_t User;
uint32_t Group;
llvm::sys::fs::file_type Type;
llvm::sys::fs::perms Perms;
Status makeStatus() const;
};
} // namespace detail
@ -428,14 +443,15 @@ class InMemoryFileSystem : public FileSystem {
std::string WorkingDirectory;
bool UseNormalizedPaths = true;
/// If HardLinkTarget is non-null, a hardlink is created to the To path which
/// must be a file. If it is null then it adds the file as the public addFile.
using MakeNodeFn = llvm::function_ref<std::unique_ptr<detail::InMemoryNode>(
detail::NewInMemoryNodeInfo)>;
/// Create node with \p MakeNode and add it into this filesystem at \p Path.
bool addFile(const Twine &Path, time_t ModificationTime,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
Optional<uint32_t> User, Optional<uint32_t> Group,
Optional<llvm::sys::fs::file_type> Type,
Optional<llvm::sys::fs::perms> Perms,
const detail::InMemoryFile *HardLinkTarget);
Optional<llvm::sys::fs::perms> Perms, MakeNodeFn MakeNode);
public:
explicit InMemoryFileSystem(bool UseNormalizedPaths = true);

View File

@ -729,6 +729,16 @@ static sys::fs::UniqueID getDirectoryID(sys::fs::UniqueID Parent,
return getUniqueID(llvm::hash_combine(Parent.getFile(), Name));
}
Status detail::NewInMemoryNodeInfo::makeStatus() const {
UniqueID UID =
(Type == sys::fs::file_type::directory_file)
? getDirectoryID(DirUID, Name)
: getFileID(DirUID, Name, Buffer ? Buffer->getBuffer() : "");
return Status(Path, UID, llvm::sys::toTimePoint(ModificationTime), User,
Group, Buffer ? Buffer->getBufferSize() : 0, Type, Perms);
}
InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths)
: Root(new detail::InMemoryDirectory(
Status("", getDirectoryID(llvm::sys::fs::UniqueID(), ""),
@ -749,7 +759,7 @@ bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
Optional<uint32_t> Group,
Optional<llvm::sys::fs::file_type> Type,
Optional<llvm::sys::fs::perms> Perms,
const detail::InMemoryFile *HardLinkTarget) {
MakeNodeFn MakeNode) {
SmallString<128> Path;
P.toVector(Path);
@ -770,7 +780,6 @@ bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
const auto ResolvedGroup = Group.getValueOr(0);
const auto ResolvedType = Type.getValueOr(sys::fs::file_type::regular_file);
const auto ResolvedPerms = Perms.getValueOr(sys::fs::all_all);
assert(!(HardLinkTarget && Buffer) && "HardLink cannot have a buffer");
// Any intermediate directories we create should be accessible by
// the owner, even if Perms says otherwise for the final path.
const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all;
@ -781,27 +790,10 @@ bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
if (!Node) {
if (I == E) {
// End of the path.
std::unique_ptr<detail::InMemoryNode> Child;
if (HardLinkTarget)
Child.reset(new detail::InMemoryHardLink(P.str(), *HardLinkTarget));
else {
// Create a new file or directory.
Status Stat(
P.str(),
(ResolvedType == sys::fs::file_type::directory_file)
? getDirectoryID(Dir->getUniqueID(), Name)
: getFileID(Dir->getUniqueID(), Name, Buffer->getBuffer()),
llvm::sys::toTimePoint(ModificationTime), ResolvedUser,
ResolvedGroup, Buffer->getBufferSize(), ResolvedType,
ResolvedPerms);
if (ResolvedType == sys::fs::file_type::directory_file) {
Child.reset(new detail::InMemoryDirectory(std::move(Stat)));
} else {
Child.reset(
new detail::InMemoryFile(std::move(Stat), std::move(Buffer)));
}
}
Dir->addChild(Name, std::move(Child));
Dir->addChild(
Name, MakeNode({Dir->getUniqueID(), Path, Name, ModificationTime,
std::move(Buffer), ResolvedUser, ResolvedGroup,
ResolvedType, ResolvedPerms}));
return true;
}
@ -845,7 +837,15 @@ bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime,
Optional<llvm::sys::fs::file_type> Type,
Optional<llvm::sys::fs::perms> Perms) {
return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type,
Perms, /*HardLinkTarget=*/nullptr);
Perms,
[](detail::NewInMemoryNodeInfo NNI)
-> std::unique_ptr<detail::InMemoryNode> {
Status Stat = NNI.makeStatus();
if (Stat.getType() == sys::fs::file_type::directory_file)
return std::make_unique<detail::InMemoryDirectory>(Stat);
return std::make_unique<detail::InMemoryFile>(
Stat, std::move(NNI.Buffer));
});
}
bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
@ -856,7 +856,15 @@ bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime,
Optional<llvm::sys::fs::perms> Perms) {
return addFile(P, ModificationTime, llvm::MemoryBuffer::getMemBuffer(Buffer),
std::move(User), std::move(Group), std::move(Type),
std::move(Perms));
std::move(Perms),
[](detail::NewInMemoryNodeInfo NNI)
-> std::unique_ptr<detail::InMemoryNode> {
Status Stat = NNI.makeStatus();
if (Stat.getType() == sys::fs::file_type::directory_file)
return std::make_unique<detail::InMemoryDirectory>(Stat);
return std::make_unique<detail::InMemoryFile>(
Stat, std::move(NNI.Buffer));
});
}
static ErrorOr<const detail::InMemoryNode *>
@ -911,8 +919,11 @@ bool InMemoryFileSystem::addHardLink(const Twine &FromPath,
// before. Resolved ToPath must be a File.
if (!ToNode || FromNode || !isa<detail::InMemoryFile>(*ToNode))
return false;
return this->addFile(FromPath, 0, nullptr, None, None, None, None,
cast<detail::InMemoryFile>(*ToNode));
return addFile(FromPath, 0, nullptr, None, None, None, None,
[&](detail::NewInMemoryNodeInfo NNI) {
return std::make_unique<detail::InMemoryHardLink>(
NNI.Path.str(), *cast<detail::InMemoryFile>(*ToNode));
});
}
llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) {