diff options
Diffstat (limited to 'lib/Basic/VirtualFileSystem.cpp')
| -rw-r--r-- | lib/Basic/VirtualFileSystem.cpp | 631 | 
1 files changed, 506 insertions, 125 deletions
diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index a36102cf0f5ab..cf5a8d681eacc 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -10,6 +10,7 @@  //===----------------------------------------------------------------------===//  #include "clang/Basic/VirtualFileSystem.h" +#include "clang/Basic/FileManager.h"  #include "llvm/ADT/DenseMap.h"  #include "llvm/ADT/STLExtras.h"  #include "llvm/ADT/StringExtras.h" @@ -19,9 +20,17 @@  #include "llvm/Support/MemoryBuffer.h"  #include "llvm/Support/Path.h"  #include "llvm/Support/YAMLParser.h" +#include "llvm/Config/llvm-config.h"  #include <atomic>  #include <memory> +// For chdir. +#ifdef LLVM_ON_WIN32 +#  include <direct.h> +#else +#  include <unistd.h> +#endif +  using namespace clang;  using namespace clang::vfs;  using namespace llvm; @@ -35,12 +44,24 @@ Status::Status(const file_status &Status)        User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),        Type(Status.type()), Perms(Status.permissions()), IsVFSMapped(false)  {} -Status::Status(StringRef Name, StringRef ExternalName, UniqueID UID, -               sys::TimeValue MTime, uint32_t User, uint32_t Group, -               uint64_t Size, file_type Type, perms Perms) +Status::Status(StringRef Name, UniqueID UID, sys::TimeValue MTime, +               uint32_t User, uint32_t Group, uint64_t Size, file_type Type, +               perms Perms)      : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size),        Type(Type), Perms(Perms), IsVFSMapped(false) {} +Status Status::copyWithNewName(const Status &In, StringRef NewName) { +  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), +                In.getUser(), In.getGroup(), In.getSize(), In.getType(), +                In.getPermissions()); +} + +Status Status::copyWithNewName(const file_status &In, StringRef NewName) { +  return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), +                In.getUser(), In.getGroup(), In.getSize(), In.type(), +                In.permissions()); +} +  bool Status::equivalent(const Status &Other) const {    return getUniqueID() == Other.getUniqueID();  } @@ -77,6 +98,19 @@ FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize,    return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile);  } +std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const { +  auto WorkingDir = getCurrentWorkingDirectory(); +  if (!WorkingDir) +    return WorkingDir.getError(); + +  return llvm::sys::fs::make_absolute(WorkingDir.get(), Path); +} + +bool FileSystem::exists(const Twine &Path) { +  auto Status = status(Path); +  return Status && Status->exists(); +} +  //===-----------------------------------------------------------------------===/  // RealFileSystem implementation  //===-----------------------------------------------------------------------===/ @@ -87,19 +121,20 @@ class RealFile : public File {    int FD;    Status S;    friend class RealFileSystem; -  RealFile(int FD) : FD(FD) { +  RealFile(int FD, StringRef NewName) +      : FD(FD), S(NewName, {}, {}, {}, {}, {}, +                  llvm::sys::fs::file_type::status_error, {}) {      assert(FD >= 0 && "Invalid or inactive file descriptor");    }  public:    ~RealFile() override;    ErrorOr<Status> status() override; -  ErrorOr<std::unique_ptr<MemoryBuffer>> -  getBuffer(const Twine &Name, int64_t FileSize = -1, -            bool RequiresNullTerminator = true, -            bool IsVolatile = false) override; +  ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name, +                                                   int64_t FileSize, +                                                   bool RequiresNullTerminator, +                                                   bool IsVolatile) override;    std::error_code close() override; -  void setName(StringRef Name) override;  };  } // end anonymous namespace  RealFile::~RealFile() { close(); } @@ -110,9 +145,7 @@ ErrorOr<Status> RealFile::status() {      file_status RealStatus;      if (std::error_code EC = sys::fs::status(FD, RealStatus))        return EC; -    Status NewS(RealStatus); -    NewS.setName(S.getName()); -    S = std::move(NewS); +    S = Status::copyWithNewName(RealStatus, S.getName());    }    return S;  } @@ -142,10 +175,6 @@ std::error_code RealFile::close() {    return std::error_code();  } -void RealFile::setName(StringRef Name) { -  S.setName(Name); -} -  namespace {  /// \brief The file system according to your operating system.  class RealFileSystem : public FileSystem { @@ -153,6 +182,9 @@ public:    ErrorOr<Status> status(const Twine &Path) override;    ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override;    directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + +  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; +  std::error_code setCurrentWorkingDirectory(const Twine &Path) override;  };  } // end anonymous namespace @@ -160,9 +192,7 @@ ErrorOr<Status> RealFileSystem::status(const Twine &Path) {    sys::fs::file_status RealStatus;    if (std::error_code EC = sys::fs::status(Path, RealStatus))      return EC; -  Status Result(RealStatus); -  Result.setName(Path.str()); -  return Result; +  return Status::copyWithNewName(RealStatus, Path.str());  }  ErrorOr<std::unique_ptr<File>> @@ -170,9 +200,29 @@ RealFileSystem::openFileForRead(const Twine &Name) {    int FD;    if (std::error_code EC = sys::fs::openFileForRead(Name, FD))      return EC; -  std::unique_ptr<File> Result(new RealFile(FD)); -  Result->setName(Name.str()); -  return std::move(Result); +  return std::unique_ptr<File>(new RealFile(FD, Name.str())); +} + +llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const { +  SmallString<256> Dir; +  if (std::error_code EC = llvm::sys::fs::current_path(Dir)) +    return EC; +  return Dir.str().str(); +} + +std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) { +  // FIXME: chdir is thread hostile; on the other hand, creating the same +  // behavior as chdir is complex: chdir resolves the path once, thus +  // guaranteeing that all subsequent relative path operations work +  // on the same path the original chdir resulted in. This makes a +  // difference for example on network filesystems, where symlinks might be +  // switched during runtime of the tool. Fixing this depends on having a +  // file system abstraction that allows openat() style interactions. +  SmallString<256> Storage; +  StringRef Dir = Path.toNullTerminatedStringRef(Storage); +  if (int Err = ::chdir(Dir.data())) +    return std::error_code(Err, std::generic_category()); +  return std::error_code();  }  IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() { @@ -190,10 +240,8 @@ public:      if (!EC && Iter != llvm::sys::fs::directory_iterator()) {        llvm::sys::fs::file_status S;        EC = Iter->status(S); -      if (!EC) { -        CurrentEntry = Status(S); -        CurrentEntry.setName(Iter->path()); -      } +      if (!EC) +        CurrentEntry = Status::copyWithNewName(S, Iter->path());      }    } @@ -207,8 +255,7 @@ public:      } else {        llvm::sys::fs::file_status S;        EC = Iter->status(S); -      CurrentEntry = Status(S); -      CurrentEntry.setName(Iter->path()); +      CurrentEntry = Status::copyWithNewName(S, Iter->path());      }      return EC;    } @@ -224,11 +271,14 @@ directory_iterator RealFileSystem::dir_begin(const Twine &Dir,  // OverlayFileSystem implementation  //===-----------------------------------------------------------------------===/  OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) { -  pushOverlay(BaseFS); +  FSList.push_back(BaseFS);  }  void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) {    FSList.push_back(FS); +  // Synchronize added file systems by duplicating the working directory from +  // the first one in the list. +  FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get());  }  ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) { @@ -252,6 +302,19 @@ OverlayFileSystem::openFileForRead(const llvm::Twine &Path) {    return make_error_code(llvm::errc::no_such_file_or_directory);  } +llvm::ErrorOr<std::string> +OverlayFileSystem::getCurrentWorkingDirectory() const { +  // All file systems are synchronized, just take the first working directory. +  return FSList.front()->getCurrentWorkingDirectory(); +} +std::error_code +OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) { +  for (auto &FS : FSList) +    if (std::error_code EC = FS->setCurrentWorkingDirectory(Path)) +      return EC; +  return std::error_code(); +} +  clang::vfs::detail::DirIterImpl::~DirIterImpl() { }  namespace { @@ -320,8 +383,286 @@ 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, bool RequiresNullTerminator, +            bool IsVolatile) 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(bool UseNormalizedPaths) +    : 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))), +      UseNormalizedPaths(UseNormalizedPaths) {} + +InMemoryFileSystem::~InMemoryFileSystem() {} + +std::string InMemoryFileSystem::toString() const { +  return Root->toString(/*Indent=*/0); +} + +bool 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 = makeAbsolute(Path); +  assert(!EC); +  (void)EC; + +  if (useNormalizedPaths()) +    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + +  if (Path.empty()) +    return false; + +  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(P.str(), getNextVirtualUniqueID(), +                    llvm::sys::TimeValue(ModificationTime, 0), 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 true; +      } + +      // 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, 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; +    } else { +      assert(isa<detail::InMemoryFile>(Node) && +             "Must be either file or directory!"); + +      // Trying to insert a directory in place of a file. +      if (I != E) +        return false; + +      // Return false only if the new file is different from the existing one. +      return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() == +             Buffer->getBuffer(); +    } +  } +} + +bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime, +                                      llvm::MemoryBuffer *Buffer) { +  return addFile(P, ModificationTime, +                 llvm::MemoryBuffer::getMemBuffer( +                     Buffer->getBuffer(), Buffer->getBufferIdentifier())); +} + +static ErrorOr<detail::InMemoryNode *> +lookupInMemoryNode(const InMemoryFileSystem &FS, 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 = FS.makeAbsolute(Path); +  assert(!EC); +  (void)EC; + +  if (FS.useNormalizedPaths()) +    llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + +  if (Path.empty()) +    return Dir; + +  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(*this, 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(*this, 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(*this, 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 +// RedirectingFileSystem implementation  //===-----------------------------------------------------------------------===/  namespace { @@ -343,23 +684,24 @@ public:    EntryKind getKind() const { return Kind; }  }; -class DirectoryEntry : public Entry { -  std::vector<Entry *> Contents; +class RedirectingDirectoryEntry : public Entry { +  std::vector<std::unique_ptr<Entry>> Contents;    Status S;  public: -  ~DirectoryEntry() override; -  DirectoryEntry(StringRef Name, std::vector<Entry *> Contents, Status S) +  RedirectingDirectoryEntry(StringRef Name, +                            std::vector<std::unique_ptr<Entry>> Contents, +                            Status S)        : Entry(EK_Directory, Name), Contents(std::move(Contents)),          S(std::move(S)) {}    Status getStatus() { return S; } -  typedef std::vector<Entry *>::iterator iterator; +  typedef decltype(Contents)::iterator iterator;    iterator contents_begin() { return Contents.begin(); }    iterator contents_end() { return Contents.end(); }    static bool classof(const Entry *E) { return E->getKind() == EK_Directory; }  }; -class FileEntry : public Entry { +class RedirectingFileEntry : public Entry {  public:    enum NameKind {      NK_NotSet, @@ -370,7 +712,8 @@ private:    std::string ExternalContentsPath;    NameKind UseName;  public: -  FileEntry(StringRef Name, StringRef ExternalContentsPath, NameKind UseName) +  RedirectingFileEntry(StringRef Name, StringRef ExternalContentsPath, +                       NameKind UseName)        : Entry(EK_File, Name), ExternalContentsPath(ExternalContentsPath),          UseName(UseName) {}    StringRef getExternalContentsPath() const { return ExternalContentsPath; } @@ -382,16 +725,18 @@ public:    static bool classof(const Entry *E) { return E->getKind() == EK_File; }  }; -class VFSFromYAML; +class RedirectingFileSystem;  class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl {    std::string Dir; -  VFSFromYAML &FS; -  DirectoryEntry::iterator Current, End; +  RedirectingFileSystem &FS; +  RedirectingDirectoryEntry::iterator Current, End; +  public: -  VFSFromYamlDirIterImpl(const Twine &Path, VFSFromYAML &FS, -                         DirectoryEntry::iterator Begin, -                         DirectoryEntry::iterator End, std::error_code &EC); +  VFSFromYamlDirIterImpl(const Twine &Path, RedirectingFileSystem &FS, +                         RedirectingDirectoryEntry::iterator Begin, +                         RedirectingDirectoryEntry::iterator End, +                         std::error_code &EC);    std::error_code increment() override;  }; @@ -448,8 +793,9 @@ public:  /// In both cases, the 'name' field may contain multiple path components (e.g.  /// /path/to/file). However, any directory that contains more than one child  /// must be uniquely represented by a directory entry. -class VFSFromYAML : public vfs::FileSystem { -  std::vector<Entry *> Roots; ///< The root(s) of the virtual file system. +class RedirectingFileSystem : public vfs::FileSystem { +  /// The root(s) of the virtual file system. +  std::vector<std::unique_ptr<Entry>> Roots;    /// \brief The file system to use for external references.    IntrusiveRefCntPtr<FileSystem> ExternalFS; @@ -466,10 +812,10 @@ class VFSFromYAML : public vfs::FileSystem {    bool UseExternalNames;    /// @} -  friend class VFSFromYAMLParser; +  friend class RedirectingFileSystemParser;  private: -  VFSFromYAML(IntrusiveRefCntPtr<FileSystem> ExternalFS) +  RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> ExternalFS)        : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {}    /// \brief Looks up \p Path in \c Roots. @@ -484,18 +830,23 @@ private:    ErrorOr<Status> status(const Twine &Path, Entry *E);  public: -  ~VFSFromYAML() override; -    /// \brief Parses \p Buffer, which is expected to be in YAML format and    /// returns a virtual file system representing its contents. -  static VFSFromYAML *create(std::unique_ptr<MemoryBuffer> Buffer, -                             SourceMgr::DiagHandlerTy DiagHandler, -                             void *DiagContext, -                             IntrusiveRefCntPtr<FileSystem> ExternalFS); +  static RedirectingFileSystem * +  create(std::unique_ptr<MemoryBuffer> Buffer, +         SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext, +         IntrusiveRefCntPtr<FileSystem> ExternalFS);    ErrorOr<Status> status(const Twine &Path) override;    ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; +  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { +    return ExternalFS->getCurrentWorkingDirectory(); +  } +  std::error_code setCurrentWorkingDirectory(const Twine &Path) override { +    return ExternalFS->setCurrentWorkingDirectory(Path); +  } +    directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override{      ErrorOr<Entry *> E = lookupPath(Dir);      if (!E) { @@ -513,14 +864,14 @@ public:        return directory_iterator();      } -    DirectoryEntry *D = cast<DirectoryEntry>(*E); +    auto *D = cast<RedirectingDirectoryEntry>(*E);      return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>(Dir,          *this, D->contents_begin(), D->contents_end(), EC));    }  };  /// \brief A helper class to hold the common YAML parsing state. -class VFSFromYAMLParser { +class RedirectingFileSystemParser {    yaml::Stream &Stream;    void error(yaml::Node *N, const Twine &Msg) { @@ -596,7 +947,7 @@ class VFSFromYAMLParser {      return true;    } -  Entry *parseEntry(yaml::Node *N) { +  std::unique_ptr<Entry> parseEntry(yaml::Node *N) {      yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);      if (!M) {        error(N, "expected mapping node for file or directory entry"); @@ -611,14 +962,13 @@ class VFSFromYAMLParser {        KeyStatusPair("use-external-name", false),      }; -    DenseMap<StringRef, KeyStatus> Keys( -        &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0])); +    DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));      bool HasContents = false; // external or otherwise -    std::vector<Entry *> EntryArrayContents; +    std::vector<std::unique_ptr<Entry>> EntryArrayContents;      std::string ExternalContentsPath;      std::string Name; -    FileEntry::NameKind UseExternalName = FileEntry::NK_NotSet; +    auto UseExternalName = RedirectingFileEntry::NK_NotSet;      EntryKind Kind;      for (yaml::MappingNode::iterator I = M->begin(), E = M->end(); I != E; @@ -667,8 +1017,8 @@ class VFSFromYAMLParser {          for (yaml::SequenceNode::iterator I = Contents->begin(),                                            E = Contents->end();               I != E; ++I) { -          if (Entry *E = parseEntry(&*I)) -            EntryArrayContents.push_back(E); +          if (std::unique_ptr<Entry> E = parseEntry(&*I)) +            EntryArrayContents.push_back(std::move(E));            else              return nullptr;          } @@ -686,7 +1036,8 @@ class VFSFromYAMLParser {          bool Val;          if (!parseScalarBool(I->getValue(), Val))            return nullptr; -        UseExternalName = Val ? FileEntry::NK_External : FileEntry::NK_Virtual; +        UseExternalName = Val ? RedirectingFileEntry::NK_External +                              : RedirectingFileEntry::NK_Virtual;        } else {          llvm_unreachable("key missing from Keys");        } @@ -704,7 +1055,8 @@ class VFSFromYAMLParser {        return nullptr;      // check invalid configuration -    if (Kind == EK_Directory && UseExternalName != FileEntry::NK_NotSet) { +    if (Kind == EK_Directory && +        UseExternalName != RedirectingFileEntry::NK_NotSet) {        error(N, "'use-external-name' is not supported for directories");        return nullptr;      } @@ -718,16 +1070,17 @@ class VFSFromYAMLParser {      // Get the last component      StringRef LastComponent = sys::path::filename(Trimmed); -    Entry *Result = nullptr; +    std::unique_ptr<Entry> Result;      switch (Kind) {      case EK_File: -      Result = new FileEntry(LastComponent, std::move(ExternalContentsPath), -                             UseExternalName); +      Result = llvm::make_unique<RedirectingFileEntry>( +          LastComponent, std::move(ExternalContentsPath), UseExternalName);        break;      case EK_Directory: -      Result = new DirectoryEntry(LastComponent, std::move(EntryArrayContents), -          Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, -                 0, file_type::directory_file, sys::fs::all_all)); +      Result = llvm::make_unique<RedirectingDirectoryEntry>( +          LastComponent, std::move(EntryArrayContents), +          Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0, +                 file_type::directory_file, sys::fs::all_all));        break;      } @@ -739,18 +1092,21 @@ class VFSFromYAMLParser {      for (sys::path::reverse_iterator I = sys::path::rbegin(Parent),                                       E = sys::path::rend(Parent);           I != E; ++I) { -      Result = new DirectoryEntry(*I, llvm::makeArrayRef(Result), -          Status("", "", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, -                 0, file_type::directory_file, sys::fs::all_all)); +      std::vector<std::unique_ptr<Entry>> Entries; +      Entries.push_back(std::move(Result)); +      Result = llvm::make_unique<RedirectingDirectoryEntry>( +          *I, std::move(Entries), +          Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0, 0, +                 file_type::directory_file, sys::fs::all_all));      }      return Result;    }  public: -  VFSFromYAMLParser(yaml::Stream &S) : Stream(S) {} +  RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {}    // false on error -  bool parse(yaml::Node *Root, VFSFromYAML *FS) { +  bool parse(yaml::Node *Root, RedirectingFileSystem *FS) {      yaml::MappingNode *Top = dyn_cast<yaml::MappingNode>(Root);      if (!Top) {        error(Root, "expected mapping node"); @@ -764,8 +1120,7 @@ public:        KeyStatusPair("roots", true),      }; -    DenseMap<StringRef, KeyStatus> Keys( -        &Fields[0], Fields + sizeof(Fields)/sizeof(Fields[0])); +    DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));      // Parse configuration and 'roots'      for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E; @@ -787,8 +1142,8 @@ public:          for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();               I != E; ++I) { -          if (Entry *E = parseEntry(&*I)) -            FS->Roots.push_back(E); +          if (std::unique_ptr<Entry> E = parseEntry(&*I)) +            FS->Roots.push_back(std::move(E));            else              return false;          } @@ -831,15 +1186,11 @@ public:  };  } // end of anonymous namespace -Entry::~Entry() {} -DirectoryEntry::~DirectoryEntry() { llvm::DeleteContainerPointers(Contents); } +Entry::~Entry() = default; -VFSFromYAML::~VFSFromYAML() { llvm::DeleteContainerPointers(Roots); } - -VFSFromYAML *VFSFromYAML::create(std::unique_ptr<MemoryBuffer> Buffer, -                                 SourceMgr::DiagHandlerTy DiagHandler, -                                 void *DiagContext, -                                 IntrusiveRefCntPtr<FileSystem> ExternalFS) { +RedirectingFileSystem *RedirectingFileSystem::create( +    std::unique_ptr<MemoryBuffer> Buffer, SourceMgr::DiagHandlerTy DiagHandler, +    void *DiagContext, IntrusiveRefCntPtr<FileSystem> ExternalFS) {    SourceMgr SM;    yaml::Stream Stream(Buffer->getMemBufferRef(), SM); @@ -852,21 +1203,22 @@ VFSFromYAML *VFSFromYAML::create(std::unique_ptr<MemoryBuffer> Buffer,      return nullptr;    } -  VFSFromYAMLParser P(Stream); +  RedirectingFileSystemParser P(Stream); -  std::unique_ptr<VFSFromYAML> FS(new VFSFromYAML(ExternalFS)); +  std::unique_ptr<RedirectingFileSystem> FS( +      new RedirectingFileSystem(ExternalFS));    if (!P.parse(Root, FS.get()))      return nullptr;    return FS.release();  } -ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) { +ErrorOr<Entry *> RedirectingFileSystem::lookupPath(const Twine &Path_) {    SmallString<256> Path;    Path_.toVector(Path);    // Handle relative paths -  if (std::error_code EC = sys::fs::make_absolute(Path)) +  if (std::error_code EC = makeAbsolute(Path))      return EC;    if (Path.empty()) @@ -874,18 +1226,17 @@ ErrorOr<Entry *> VFSFromYAML::lookupPath(const Twine &Path_) {    sys::path::const_iterator Start = sys::path::begin(Path);    sys::path::const_iterator End = sys::path::end(Path); -  for (std::vector<Entry *>::iterator I = Roots.begin(), E = Roots.end(); -       I != E; ++I) { -    ErrorOr<Entry *> Result = lookupPath(Start, End, *I); +  for (const std::unique_ptr<Entry> &Root : Roots) { +    ErrorOr<Entry *> Result = lookupPath(Start, End, Root.get());      if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)        return Result;    }    return make_error_code(llvm::errc::no_such_file_or_directory);  } -ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start, -                                         sys::path::const_iterator End, -                                         Entry *From) { +ErrorOr<Entry *> +RedirectingFileSystem::lookupPath(sys::path::const_iterator Start, +                                  sys::path::const_iterator End, Entry *From) {    if (Start->equals("."))      ++Start; @@ -902,52 +1253,78 @@ ErrorOr<Entry *> VFSFromYAML::lookupPath(sys::path::const_iterator Start,      return From;    } -  DirectoryEntry *DE = dyn_cast<DirectoryEntry>(From); +  auto *DE = dyn_cast<RedirectingDirectoryEntry>(From);    if (!DE)      return make_error_code(llvm::errc::not_a_directory); -  for (DirectoryEntry::iterator I = DE->contents_begin(), -                                E = DE->contents_end(); -       I != E; ++I) { -    ErrorOr<Entry *> Result = lookupPath(Start, End, *I); +  for (const std::unique_ptr<Entry> &DirEntry : +       llvm::make_range(DE->contents_begin(), DE->contents_end())) { +    ErrorOr<Entry *> Result = lookupPath(Start, End, DirEntry.get());      if (Result || Result.getError() != llvm::errc::no_such_file_or_directory)        return Result;    }    return make_error_code(llvm::errc::no_such_file_or_directory);  } -ErrorOr<Status> VFSFromYAML::status(const Twine &Path, Entry *E) { +static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames, +                                      Status ExternalStatus) { +  Status S = ExternalStatus; +  if (!UseExternalNames) +    S = Status::copyWithNewName(S, Path.str()); +  S.IsVFSMapped = true; +  return S; +} + +ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, Entry *E) {    assert(E != nullptr); -  std::string PathStr(Path.str()); -  if (FileEntry *F = dyn_cast<FileEntry>(E)) { +  if (auto *F = dyn_cast<RedirectingFileEntry>(E)) {      ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath());      assert(!S || S->getName() == F->getExternalContentsPath()); -    if (S && !F->useExternalName(UseExternalNames)) -      S->setName(PathStr);      if (S) -      S->IsVFSMapped = true; +      return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), +                                     *S);      return S;    } else { // directory -    DirectoryEntry *DE = cast<DirectoryEntry>(E); -    Status S = DE->getStatus(); -    S.setName(PathStr); -    return S; +    auto *DE = cast<RedirectingDirectoryEntry>(E); +    return Status::copyWithNewName(DE->getStatus(), Path.str());    }  } -ErrorOr<Status> VFSFromYAML::status(const Twine &Path) { +ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) {    ErrorOr<Entry *> Result = lookupPath(Path);    if (!Result)      return Result.getError();    return status(Path, *Result);  } -ErrorOr<std::unique_ptr<File>> VFSFromYAML::openFileForRead(const Twine &Path) { +namespace { +/// Provide a file wrapper with an overriden status. +class FileWithFixedStatus : public File { +  std::unique_ptr<File> InnerFile; +  Status S; + +public: +  FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S) +      : InnerFile(std::move(InnerFile)), S(S) {} + +  ErrorOr<Status> status() override { return S; } +  ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> +  getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, +            bool IsVolatile) override { +    return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator, +                                IsVolatile); +  } +  std::error_code close() override { return InnerFile->close(); } +}; +} // end anonymous namespace + +ErrorOr<std::unique_ptr<File>> +RedirectingFileSystem::openFileForRead(const Twine &Path) {    ErrorOr<Entry *> E = lookupPath(Path);    if (!E)      return E.getError(); -  FileEntry *F = dyn_cast<FileEntry>(*E); +  auto *F = dyn_cast<RedirectingFileEntry>(*E);    if (!F) // FIXME: errc::not_a_file?      return make_error_code(llvm::errc::invalid_argument); @@ -955,18 +1332,23 @@ ErrorOr<std::unique_ptr<File>> VFSFromYAML::openFileForRead(const Twine &Path) {    if (!Result)      return Result; -  if (!F->useExternalName(UseExternalNames)) -    (*Result)->setName(Path.str()); +  auto ExternalStatus = (*Result)->status(); +  if (!ExternalStatus) +    return ExternalStatus.getError(); -  return Result; +  // FIXME: Update the status with the name and VFSMapped. +  Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), +                                     *ExternalStatus); +  return std::unique_ptr<File>( +      llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S));  }  IntrusiveRefCntPtr<FileSystem>  vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer,                      SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext,                      IntrusiveRefCntPtr<FileSystem> ExternalFS) { -  return VFSFromYAML::create(std::move(Buffer), DiagHandler, DiagContext, -                             ExternalFS); +  return RedirectingFileSystem::create(std::move(Buffer), DiagHandler, +                                       DiagContext, ExternalFS);  }  UniqueID vfs::getNextVirtualUniqueID() { @@ -1111,11 +1493,10 @@ void YAMLVFSWriter::write(llvm::raw_ostream &OS) {    JSONWriter(OS).write(Mappings, IsCaseSensitive);  } -VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl(const Twine &_Path, -                                               VFSFromYAML &FS, -                                               DirectoryEntry::iterator Begin, -                                               DirectoryEntry::iterator End, -                                               std::error_code &EC) +VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl( +    const Twine &_Path, RedirectingFileSystem &FS, +    RedirectingDirectoryEntry::iterator Begin, +    RedirectingDirectoryEntry::iterator End, std::error_code &EC)      : Dir(_Path.str()), FS(FS), Current(Begin), End(End) {    if (Current != End) {      SmallString<128> PathStr(Dir);  | 
