diff options
Diffstat (limited to 'clang/lib/Basic/VirtualFileSystem.cpp')
-rw-r--r-- | clang/lib/Basic/VirtualFileSystem.cpp | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/clang/lib/Basic/VirtualFileSystem.cpp b/clang/lib/Basic/VirtualFileSystem.cpp index 32e485f6c2e..b258bc42523 100644 --- a/clang/lib/Basic/VirtualFileSystem.cpp +++ b/clang/lib/Basic/VirtualFileSystem.cpp @@ -320,6 +320,252 @@ directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir, std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC)); } +namespace clang { +namespace vfs { +namespace detail { + +enum InMemoryNodeKind { IME_File, IME_Directory }; + +/// The in memory file system is a tree of Nodes. Every node can either be a +/// file or a directory. +class InMemoryNode { + Status Stat; + InMemoryNodeKind Kind; + +public: + InMemoryNode(Status Stat, InMemoryNodeKind Kind) + : Stat(std::move(Stat)), Kind(Kind) {} + virtual ~InMemoryNode() {} + const Status &getStatus() const { return Stat; } + InMemoryNodeKind getKind() const { return Kind; } + virtual std::string toString(unsigned Indent) const = 0; +}; + +namespace { +class InMemoryFile : public InMemoryNode { + std::unique_ptr<llvm::MemoryBuffer> Buffer; + +public: + InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer) + : InMemoryNode(std::move(Stat), IME_File), Buffer(std::move(Buffer)) {} + + llvm::MemoryBuffer *getBuffer() { return Buffer.get(); } + std::string toString(unsigned Indent) const override { + return (std::string(Indent, ' ') + getStatus().getName() + "\n").str(); + } + static bool classof(const InMemoryNode *N) { + return N->getKind() == IME_File; + } +}; + +/// Adapt a InMemoryFile for VFS' File interface. +class InMemoryFileAdaptor : public File { + InMemoryFile &Node; + +public: + explicit InMemoryFileAdaptor(InMemoryFile &Node) : Node(Node) {} + + llvm::ErrorOr<Status> status() override { return Node.getStatus(); } + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> + getBuffer(const Twine &Name, int64_t FileSize = -1, + bool RequiresNullTerminator = true, + bool IsVolatile = false) override { + llvm::MemoryBuffer *Buf = Node.getBuffer(); + return llvm::MemoryBuffer::getMemBuffer( + Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator); + } + std::error_code close() override { return std::error_code(); } +}; +} // end anonymous namespace + +class InMemoryDirectory : public InMemoryNode { + std::map<std::string, std::unique_ptr<InMemoryNode>> Entries; + +public: + InMemoryDirectory(Status Stat) + : InMemoryNode(std::move(Stat), IME_Directory) {} + InMemoryNode *getChild(StringRef Name) { + auto I = Entries.find(Name); + if (I != Entries.end()) + return I->second.get(); + return nullptr; + } + InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) { + return Entries.insert(make_pair(Name, std::move(Child))) + .first->second.get(); + } + + typedef decltype(Entries)::const_iterator const_iterator; + const_iterator begin() const { return Entries.begin(); } + const_iterator end() const { return Entries.end(); } + + std::string toString(unsigned Indent) const override { + std::string Result = + (std::string(Indent, ' ') + getStatus().getName() + "\n").str(); + for (const auto &Entry : Entries) { + Result += Entry.second->toString(Indent + 2); + } + return Result; + } + static bool classof(const InMemoryNode *N) { + return N->getKind() == IME_Directory; + } +}; +} + +InMemoryFileSystem::InMemoryFileSystem() + : Root(new detail::InMemoryDirectory( + Status("", getNextVirtualUniqueID(), llvm::sys::TimeValue::MinTime(), + 0, 0, 0, llvm::sys::fs::file_type::directory_file, + llvm::sys::fs::perms::all_all))) {} + +InMemoryFileSystem::~InMemoryFileSystem() {} + +StringRef InMemoryFileSystem::toString() const { + return Root->toString(/*Indent=*/0); +} + +void InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, + std::unique_ptr<llvm::MemoryBuffer> Buffer) { + SmallString<128> Path; + P.toVector(Path); + + // Fix up relative paths. This just prepends the current working directory. + std::error_code EC = sys::fs::make_absolute(Path); + assert(!EC); + (void)EC; + + detail::InMemoryDirectory *Dir = Root.get(); + auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path); + while (true) { + StringRef Name = *I; + detail::InMemoryNode *Node = Dir->getChild(Name); + ++I; + if (!Node) { + if (I == E) { + // End of the path, create a new file. + // FIXME: expose the status details in the interface. + Status Stat(Path, getNextVirtualUniqueID(), + llvm::sys::TimeValue(ModificationTime), 0, 0, + Buffer->getBufferSize(), + llvm::sys::fs::file_type::regular_file, + llvm::sys::fs::all_all); + Dir->addChild(Name, llvm::make_unique<detail::InMemoryFile>( + std::move(Stat), std::move(Buffer))); + return; + } + + // Create a new directory. Use the path up to here. + // FIXME: expose the status details in the interface. + Status Stat( + StringRef(Path.str().begin(), Name.end() - Path.str().begin()), + getNextVirtualUniqueID(), llvm::sys::TimeValue(ModificationTime), 0, + 0, Buffer->getBufferSize(), llvm::sys::fs::file_type::directory_file, + llvm::sys::fs::all_all); + Dir = cast<detail::InMemoryDirectory>(Dir->addChild( + Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat)))); + continue; + } + + if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) + Dir = NewDir; + } +} + +static ErrorOr<detail::InMemoryNode *> +lookupInMemoryNode(detail::InMemoryDirectory *Dir, const Twine &P) { + SmallString<128> Path; + P.toVector(Path); + + // Fix up relative paths. This just prepends the current working directory. + std::error_code EC = sys::fs::make_absolute(Path); + assert(!EC); + (void)EC; + + auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path); + while (true) { + detail::InMemoryNode *Node = Dir->getChild(*I); + ++I; + if (!Node) + return errc::no_such_file_or_directory; + + // Return the file if it's at the end of the path. + if (auto File = dyn_cast<detail::InMemoryFile>(Node)) { + if (I == E) + return File; + return errc::no_such_file_or_directory; + } + + // Traverse directories. + Dir = cast<detail::InMemoryDirectory>(Node); + if (I == E) + return Dir; + } +} + +llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) { + auto Node = lookupInMemoryNode(Root.get(), Path); + if (Node) + return (*Node)->getStatus(); + return Node.getError(); +} + +llvm::ErrorOr<std::unique_ptr<File>> +InMemoryFileSystem::openFileForRead(const Twine &Path) { + auto Node = lookupInMemoryNode(Root.get(), Path); + if (!Node) + return Node.getError(); + + // When we have a file provide a heap-allocated wrapper for the memory buffer + // to match the ownership semantics for File. + if (auto *F = dyn_cast<detail::InMemoryFile>(*Node)) + return std::unique_ptr<File>(new detail::InMemoryFileAdaptor(*F)); + + // FIXME: errc::not_a_file? + return make_error_code(llvm::errc::invalid_argument); +} + +namespace { +/// Adaptor from InMemoryDir::iterator to directory_iterator. +class InMemoryDirIterator : public clang::vfs::detail::DirIterImpl { + detail::InMemoryDirectory::const_iterator I; + detail::InMemoryDirectory::const_iterator E; + +public: + InMemoryDirIterator() {} + explicit InMemoryDirIterator(detail::InMemoryDirectory &Dir) + : I(Dir.begin()), E(Dir.end()) { + if (I != E) + CurrentEntry = I->second->getStatus(); + } + + std::error_code increment() override { + ++I; + // When we're at the end, make CurrentEntry invalid and DirIterImpl will do + // the rest. + CurrentEntry = I != E ? I->second->getStatus() : Status(); + return std::error_code(); + } +}; +} // end anonymous namespace + +directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir, + std::error_code &EC) { + auto Node = lookupInMemoryNode(Root.get(), Dir); + if (!Node) { + EC = Node.getError(); + return directory_iterator(std::make_shared<InMemoryDirIterator>()); + } + + if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node)) + return directory_iterator(std::make_shared<InMemoryDirIterator>(*DirNode)); + + EC = make_error_code(llvm::errc::not_a_directory); + return directory_iterator(std::make_shared<InMemoryDirIterator>()); +} +} +} + //===-----------------------------------------------------------------------===/ // VFSFromYAML implementation //===-----------------------------------------------------------------------===/ |