- Use ErrorOr in places where it makes sense to prevent using an invalid 
object
    - Dodge the question of how to write the predicate functions by removing 
them all from AbstractFileSystem, since only the ones in Status are being 
called for now
    - De-virtualize getBufferForFile(Path) by using getBufferForOpenFile(FD).  
This is what MemoryBuffer was doing internally anway.
    - To support the previous point, I added a close(FD) method. Right now this 
requires an ugly #include to get ::close, but I will move this into 
llvm::sys::fs. For now it is still a net improvement, since the FileManager now 
doesn't call raw ::close.
    - Rename functions in Status to follow naming conventions

Hi doug.gregor, klimek, gribozavr,

http://llvm-reviews.chandlerc.com/D2745

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D2745?vs=7006&id=7095#toc

Files:
  include/clang/Basic/FileManager.h
  include/clang/Basic/FileSystemStatCache.h
  include/clang/Basic/VirtualFileSystem.h
  include/clang/Frontend/CompilerInstance.h
  lib/Basic/CMakeLists.txt
  lib/Basic/FileManager.cpp
  lib/Basic/FileSystemStatCache.cpp
  lib/Basic/VirtualFileSystem.cpp
  lib/Frontend/ASTUnit.cpp
  lib/Frontend/CacheTokens.cpp
  lib/Frontend/ChainedIncludesSource.cpp
  lib/Frontend/CompilerInstance.cpp
  lib/Frontend/FrontendAction.cpp
  lib/Lex/PTHLexer.cpp
  unittests/Basic/FileManagerTest.cpp
Index: include/clang/Basic/FileManager.h
===================================================================
--- include/clang/Basic/FileManager.h
+++ include/clang/Basic/FileManager.h
@@ -17,14 +17,14 @@
 
 #include "clang/Basic/FileSystemOptions.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/OwningPtr.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Allocator.h"
-#include "llvm/Support/FileSystem.h"
 // FIXME: Enhance libsystem to support inode and other fields in stat.
 #include <sys/types.h>
 
@@ -66,19 +66,22 @@
   llvm::sys::fs::UniqueID UniqueID;
   bool IsNamedPipe;
   bool InPCH;
+  AbstractFileSystem *FS;
 
   /// FD - The file descriptor for the file entry if it is opened and owned
   /// by the FileEntry.  If not, this is set to -1.
-  mutable int FD;
+  mutable AbstractFileSystem::FileDescriptor FD;
   friend class FileManager;
 
 public:
-  FileEntry(llvm::sys::fs::UniqueID UniqueID, bool IsNamedPipe, bool InPCH)
+  FileEntry(llvm::sys::fs::UniqueID UniqueID, bool IsNamedPipe, bool InPCH,
+            AbstractFileSystem *FS)
       : Name(0), UniqueID(UniqueID), IsNamedPipe(IsNamedPipe), InPCH(InPCH),
-        FD(-1) {}
+        FS(FS), FD(-1) {}
   // Add a default constructor for use with llvm::StringMap
   FileEntry()
-      : Name(0), UniqueID(0, 0), IsNamedPipe(false), InPCH(false), FD(-1) {}
+      : Name(0), UniqueID(0, 0), IsNamedPipe(false), InPCH(false), FS(0), FD(-1)
+  {}
 
   FileEntry(const FileEntry &FE) {
     memcpy(this, &FE, sizeof(FE));
@@ -119,6 +122,7 @@
 /// as a single file.
 ///
 class FileManager : public RefCountedBase<FileManager> {
+  IntrusiveRefCntPtr<AbstractFileSystem> FS;
   FileSystemOptions FileSystemOpts;
 
   class UniqueDirContainer;
@@ -179,7 +183,8 @@
   void addAncestorsAsVirtualDirs(StringRef Path);
 
 public:
-  FileManager(const FileSystemOptions &FileSystemOpts);
+  FileManager(const FileSystemOptions &FileSystemOpts,
+              llvm::IntrusiveRefCntPtr<AbstractFileSystem> FS = 0);
   ~FileManager();
 
   /// \brief Installs the provided FileSystemStatCache object within
@@ -248,7 +253,7 @@
   ///
   /// \returns false on success, true on error.
   bool getNoncachedStatValue(StringRef Path,
-                             llvm::sys::fs::file_status &Result);
+                             AbstractFileSystem::Status &Result);
 
   /// \brief Remove the real file \p Entry from the cache.
   void invalidateCache(const FileEntry *Entry);
Index: include/clang/Basic/FileSystemStatCache.h
===================================================================
--- include/clang/Basic/FileSystemStatCache.h
+++ include/clang/Basic/FileSystemStatCache.h
@@ -24,6 +24,8 @@
 
 namespace clang {
 
+class AbstractFileSystem;
+
 struct FileData {
   uint64_t Size;
   time_t ModTime;
@@ -60,7 +62,8 @@
   /// implementation can optionally fill in FileDescriptor with a valid
   /// descriptor and the client guarantees that it will close it.
   static bool get(const char *Path, FileData &Data, bool isFile,
-                  int *FileDescriptor, FileSystemStatCache *Cache);
+                  int *FileDescriptor, FileSystemStatCache *Cache,
+                  AbstractFileSystem &FS);
 
   /// \brief Sets the next stat call cache in the chain of stat caches.
   /// Takes ownership of the given stat cache.
@@ -78,17 +81,17 @@
   
 protected:
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor) = 0;
+                               int *FileDescriptor, AbstractFileSystem &FS) = 0;
 
   LookupResult statChained(const char *Path, FileData &Data, bool isFile,
-                           int *FileDescriptor) {
+                           int *FileDescriptor, AbstractFileSystem &FS) {
     if (FileSystemStatCache *Next = getNextStatCache())
-      return Next->getStat(Path, Data, isFile, FileDescriptor);
+      return Next->getStat(Path, Data, isFile, FileDescriptor, FS);
 
     // If we hit the end of the list of stat caches to try, just compute and
     // return it without a cache.
-    return get(Path, Data, isFile, FileDescriptor, 0) ? CacheMissing
-                                                      : CacheExists;
+    return get(Path, Data, isFile, FileDescriptor, 0, FS) ? CacheMissing
+                                                          : CacheExists;
   }
 };
 
@@ -107,7 +110,7 @@
   iterator end() const { return StatCalls.end(); }
 
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor);
+                               int *FileDescriptor, AbstractFileSystem &FS);
 };
 
 } // end namespace clang
Index: include/clang/Basic/VirtualFileSystem.h
===================================================================
--- /dev/null
+++ include/clang/Basic/VirtualFileSystem.h
@@ -0,0 +1,146 @@
+//===- VirtualFileSystem.h - Virtual File System Layer ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// \brief Defines the virtual file system interface AbstractFileSystem.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
+#define LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
+
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ErrorOr.h"
+
+namespace llvm {
+template <typename T> class OwningPtr;
+class MemoryBuffer;
+}
+
+namespace clang {
+
+/// \brief The virtual file system interface, modeled on llvm::sys::fs.
+class AbstractFileSystem : public llvm::RefCountedBase<AbstractFileSystem> {
+public:
+  /// \brief The result of a \p status operation.
+  class Status {
+    std::string Name;
+    std::string ExternalName;
+    llvm::sys::fs::UniqueID UID;
+    llvm::sys::TimeValue MTime;
+    uint32_t User;
+    uint32_t Group;
+    uint64_t Size;
+    llvm::sys::fs::file_type Type;
+    llvm::sys::fs::perms Perms;
+
+  public:
+    Status() : Type(llvm::sys::fs::file_type::status_error) {}
+    Status(const llvm::sys::fs::file_status &Status);
+    Status(llvm::StringRef Name, llvm::StringRef RealName,
+           llvm::sys::fs::UniqueID UID, llvm::sys::TimeValue MTime,
+           uint32_t User, uint32_t Group, uint64_t Size,
+           llvm::sys::fs::file_type Type, llvm::sys::fs::perms Perms);
+
+    /// \brief Returns the name this status was looked up by.
+    llvm::StringRef getName() const { return Name; }
+
+    /// \brief Returns the name to use outside the compiler.
+    ///
+    /// For example, in diagnostics or debug info we should use this name.
+    llvm::StringRef getExternalName() const { return ExternalName; }
+
+    void setName(llvm::StringRef N) { Name = N; }
+    void setExternalName(llvm::StringRef N) { ExternalName = N; }
+
+    /// @name Status interface from llvm::sys::fs
+    /// @{
+    llvm::sys::fs::file_type getType() const { return Type; }
+    llvm::sys::fs::perms getPermissions() const { return Perms; }
+    llvm::sys::TimeValue getLastModificationTime() const { return MTime; }
+    llvm::sys::fs::UniqueID getUniqueID() const { return UID; }
+    uint32_t getUser() const { return User; }
+    uint32_t getGroup() const { return Group; }
+    uint64_t getSize() const { return Size; }
+    void setType(llvm::sys::fs::file_type v) { Type = v; }
+    void setPermissions(llvm::sys::fs::perms p) { Perms = p; }
+    /// @}
+    /// @name Status queries
+    /// These are static queries in llvm::sys::fs.
+    /// @{
+    bool equivalent(const Status &Other) const;
+    bool isDirectory() const;
+    bool isRegularFile() const;
+    bool isOther() const;
+    bool isSymlink() const;
+    bool isStatusKnown() const;
+    bool exists() const;
+    /// }
+  };
+
+  /// \brief A type representing an open file.
+  /// FIXME: right now we only support real files, so this is just an alias for
+  /// a raw operating system file descriptor.
+  typedef int FileDescriptor;
+
+  virtual ~AbstractFileSystem();
+
+  /// @}
+  /// @name Operations on open files
+  /// This is the public interface. \see{Open file interface} for the interface
+  /// for subclasses.
+  /// @{
+  llvm::ErrorOr<Status> status(FileDescriptor FD);
+  llvm::error_code getBufferForFile(FileDescriptor FD, const llvm::Twine &Name,
+                                    llvm::OwningPtr<llvm::MemoryBuffer> &Result,
+                                    int64_t FileSize = -1,
+                                    bool RequiresNullTerminator = true);
+  llvm::error_code close(FileDescriptor FD);
+  /// @}
+  /// @name Virtual file system interface
+  /// @{
+  virtual llvm::ErrorOr<Status> status(const llvm::Twine &Path) = 0;
+  virtual llvm::ErrorOr<FileDescriptor>
+  openFileForRead(const llvm::Twine &Path) = 0;
+  /// @}
+  /// @name Convenience methods
+  /// @{
+  llvm::error_code getBufferForFile(const llvm::Twine &Name,
+                                    llvm::OwningPtr<llvm::MemoryBuffer> &Result,
+                                    int64_t FileSize = -1,
+                                    bool RequiresNullTerminator = true);
+  /// @}
+
+protected:
+  /// @name Open file interface
+  /// These methods are intended to be implemented by file systems that provide
+  /// a source of openable files. FIXME: right now only \p RealFileSystem does
+  /// this.
+  /// @{
+
+  /// \brief Get the \p Status of an open file.
+  virtual llvm::ErrorOr<Status> statusOfOpenFile(FileDescriptor FD);
+
+  /// \brief Get a \p MemoryBuffer for an open file.
+  virtual llvm::error_code
+  getBufferForOpenFile(FileDescriptor FD, const llvm::Twine &Name,
+                       llvm::OwningPtr<llvm::MemoryBuffer> &Result,
+                       int64_t FileSize = -1,
+                       bool RequiresNullTerminator = true);
+
+  /// \brief Close an open file descriptor.
+  virtual llvm::error_code closeOpenFile(FileDescriptor FD);
+  /// @}
+};
+
+/// \brief Gets an \p AbstractFileSystem for the 'real' file system, as seen by
+/// the operating system.
+llvm::IntrusiveRefCntPtr<AbstractFileSystem> getRealFileSystem();
+
+} // end namespace clang
+#endif // LLVM_CLANG_BASIC_VIRTUAL_FILE_SYSTEM_H
Index: include/clang/Frontend/CompilerInstance.h
===================================================================
--- include/clang/Frontend/CompilerInstance.h
+++ include/clang/Frontend/CompilerInstance.h
@@ -75,6 +75,9 @@
   /// The target being compiled for.
   IntrusiveRefCntPtr<TargetInfo> Target;
 
+  /// The virtual file system.
+  IntrusiveRefCntPtr<AbstractFileSystem> VirtualFileSystem;
+
   /// The file manager.
   IntrusiveRefCntPtr<FileManager> FileMgr;
 
@@ -314,6 +317,23 @@
   void setTarget(TargetInfo *Value);
 
   /// }
+  /// @name Virtual File System
+  /// {
+
+  bool hasVirtualFileSystem() const { return VirtualFileSystem != 0; }
+
+  AbstractFileSystem &getVirtualFileSystem() const {
+    assert(hasVirtualFileSystem() &&
+           "Compiler instance has no virtual file system");
+    return *VirtualFileSystem;
+  }
+
+  /// \brief Replace the current virtual file system.
+  void setVirtualFileSystem(IntrusiveRefCntPtr<AbstractFileSystem> FS) {
+    VirtualFileSystem = FS;
+  }
+
+  /// }
   /// @name File Manager
   /// {
 
@@ -527,6 +547,10 @@
                     bool ShouldOwnClient = true,
                     const CodeGenOptions *CodeGenOpts = 0);
 
+  /// Create a virtual file system and replace any existing one with it.
+  /// The default is to use the real file system.
+  void createVirtualFileSystem();
+
   /// Create the file manager and replace any existing one with it.
   void createFileManager();
 
Index: lib/Basic/CMakeLists.txt
===================================================================
--- lib/Basic/CMakeLists.txt
+++ lib/Basic/CMakeLists.txt
@@ -23,6 +23,7 @@
   TokenKinds.cpp
   Version.cpp
   VersionTuple.cpp
+  VirtualFileSystem.cpp
   )
 
 # Determine Subversion revision.
Index: lib/Basic/FileManager.cpp
===================================================================
--- lib/Basic/FileManager.cpp
+++ lib/Basic/FileManager.cpp
@@ -30,19 +30,6 @@
 #include <set>
 #include <string>
 
-// FIXME: This is terrible, we need this for ::close.
-#if !defined(_MSC_VER) && !defined(__MINGW32__)
-#include <unistd.h>
-#include <sys/uio.h>
-#else
-#include <io.h>
-#ifndef S_ISFIFO
-#define S_ISFIFO(x) (0)
-#endif
-#endif
-#if defined(LLVM_ON_UNIX)
-#include <limits.h>
-#endif
 using namespace clang;
 
 // FIXME: Enhance libsystem to support inode and other fields.
@@ -60,7 +47,7 @@
 FileEntry::~FileEntry() {
   // If this FileEntry owns an open file descriptor that never got used, close
   // it.
-  if (FD != -1) ::close(FD);
+  if (FD != -1) FS->close(FD);
 }
 
 class FileManager::UniqueDirContainer {
@@ -87,9 +74,9 @@
   /// there is already one; otherwise create and return a
   /// default-constructed FileEntry.
   FileEntry &getFile(llvm::sys::fs::UniqueID UniqueID, bool IsNamedPipe,
-                     bool InPCH) {
+                     bool InPCH, AbstractFileSystem *FS) {
     return const_cast<FileEntry &>(
-        *UniqueFiles.insert(FileEntry(UniqueID, IsNamedPipe, InPCH)).first);
+        *UniqueFiles.insert(FileEntry(UniqueID, IsNamedPipe, InPCH, FS)).first);
   }
 
   size_t size() const { return UniqueFiles.size(); }
@@ -101,13 +88,19 @@
 // Common logic.
 //===----------------------------------------------------------------------===//
 
-FileManager::FileManager(const FileSystemOptions &FSO)
-  : FileSystemOpts(FSO),
+FileManager::FileManager(const FileSystemOptions &FSO,
+                         IntrusiveRefCntPtr<AbstractFileSystem> FS)
+  : FS(FS), FileSystemOpts(FSO),
     UniqueRealDirs(*new UniqueDirContainer()),
     UniqueRealFiles(*new UniqueFileContainer()),
     SeenDirEntries(64), SeenFileEntries(64), NextFileUID(0) {
   NumDirLookups = NumFileLookups = 0;
   NumDirCacheMisses = NumFileCacheMisses = 0;
+
+  // If the caller doesn't provide a virtual file system, just grab the real
+  // file system.
+  if (!FS)
+    this->FS = getRealFileSystem();
 }
 
 FileManager::~FileManager() {
@@ -321,20 +314,21 @@
   }
 
   if (FileDescriptor != -1 && !openFile) {
-    close(FileDescriptor);
+    FS->close(FileDescriptor);
     FileDescriptor = -1;
   }
 
   // It exists.  See if we have already opened a file with the same inode.
   // This occurs when one dir is symlinked to another, for example.
   FileEntry &UFE =
-      UniqueRealFiles.getFile(Data.UniqueID, Data.IsNamedPipe, Data.InPCH);
+      UniqueRealFiles.getFile(Data.UniqueID, Data.IsNamedPipe, Data.InPCH,
+                              FS.getPtr());
 
   NamedFileEnt.setValue(&UFE);
   if (UFE.getName()) { // Already have an entry with this inode, return it.
     // If the stat process opened the file, close it to avoid a FD leak.
     if (FileDescriptor != -1)
-      close(FileDescriptor);
+      FS->close(FileDescriptor);
 
     return &UFE;
   }
@@ -386,15 +380,16 @@
   if (getStatValue(InterndFileName, Data, true, 0) == 0) {
     Data.Size = Size;
     Data.ModTime = ModificationTime;
-    UFE = &UniqueRealFiles.getFile(Data.UniqueID, Data.IsNamedPipe, Data.InPCH);
+    UFE = &UniqueRealFiles.getFile(Data.UniqueID, Data.IsNamedPipe, Data.InPCH,
+                                   FS.getPtr());
 
     NamedFileEnt.setValue(UFE);
 
     // If we had already opened this file, close it now so we don't
     // leak the descriptor. We're not going to use the file
     // descriptor anyway, since this is a virtual file.
     if (UFE->FD != -1) {
-      close(UFE->FD);
+      FS->close(UFE->FD);
       UFE->FD = -1;
     }
 
@@ -445,27 +440,27 @@
   const char *Filename = Entry->getName();
   // If the file is already open, use the open file descriptor.
   if (Entry->FD != -1) {
-    ec = llvm::MemoryBuffer::getOpenFile(Entry->FD, Filename, Result, FileSize);
+    ec = FS->getBufferForFile(Entry->FD, Filename, Result, FileSize);
     if (ErrorStr)
       *ErrorStr = ec.message();
 
-    close(Entry->FD);
+    FS->close(Entry->FD);
     Entry->FD = -1;
     return Result.take();
   }
 
   // Otherwise, open the file.
 
   if (FileSystemOpts.WorkingDir.empty()) {
-    ec = llvm::MemoryBuffer::getFile(Filename, Result, FileSize);
+    ec = FS->getBufferForFile(Filename, Result, FileSize);
     if (ec && ErrorStr)
       *ErrorStr = ec.message();
     return Result.take();
   }
 
   SmallString<128> FilePath(Entry->getName());
   FixupRelativePath(FilePath);
-  ec = llvm::MemoryBuffer::getFile(FilePath.str(), Result, FileSize);
+  ec = FS->getBufferForFile(FilePath.str(), Result, FileSize);
   if (ec && ErrorStr)
     *ErrorStr = ec.message();
   return Result.take();
@@ -476,15 +471,15 @@
   OwningPtr<llvm::MemoryBuffer> Result;
   llvm::error_code ec;
   if (FileSystemOpts.WorkingDir.empty()) {
-    ec = llvm::MemoryBuffer::getFile(Filename, Result);
+    ec = FS->getBufferForFile(Filename, Result);
     if (ec && ErrorStr)
       *ErrorStr = ec.message();
     return Result.take();
   }
 
   SmallString<128> FilePath(Filename);
   FixupRelativePath(FilePath);
-  ec = llvm::MemoryBuffer::getFile(FilePath.c_str(), Result);
+  ec = FS->getBufferForFile(FilePath.c_str(), Result);
   if (ec && ErrorStr)
     *ErrorStr = ec.message();
   return Result.take();
@@ -501,21 +496,25 @@
   // absolute!
   if (FileSystemOpts.WorkingDir.empty())
     return FileSystemStatCache::get(Path, Data, isFile, FileDescriptor,
-                                    StatCache.get());
+                                    StatCache.get(), *FS);
 
   SmallString<128> FilePath(Path);
   FixupRelativePath(FilePath);
 
   return FileSystemStatCache::get(FilePath.c_str(), Data, isFile,
-                                  FileDescriptor, StatCache.get());
+                                  FileDescriptor, StatCache.get(), *FS);
 }
 
 bool FileManager::getNoncachedStatValue(StringRef Path,
-                                        llvm::sys::fs::file_status &Result) {
+                                        AbstractFileSystem::Status &Result) {
   SmallString<128> FilePath(Path);
   FixupRelativePath(FilePath);
 
-  return llvm::sys::fs::status(FilePath.c_str(), Result);
+  llvm::ErrorOr<AbstractFileSystem::Status> S = FS->status(FilePath.c_str());
+  if (!S)
+    return false;
+  Result = *S;
+  return true;
 }
 
 void FileManager::invalidateCache(const FileEntry *Entry) {
Index: lib/Basic/FileSystemStatCache.cpp
===================================================================
--- lib/Basic/FileSystemStatCache.cpp
+++ lib/Basic/FileSystemStatCache.cpp
@@ -12,7 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Basic/FileSystemStatCache.h"
-#include "llvm/Support/FileSystem.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "llvm/Support/Path.h"
 
 // FIXME: This is terrible, we need this for ::close.
@@ -30,13 +30,13 @@
 
 void FileSystemStatCache::anchor() { }
 
-static void copyStatusToFileData(const llvm::sys::fs::file_status &Status,
+static void copyStatusToFileData(const AbstractFileSystem::Status &Status,
                                  FileData &Data) {
   Data.Size = Status.getSize();
   Data.ModTime = Status.getLastModificationTime().toEpochTime();
   Data.UniqueID = Status.getUniqueID();
-  Data.IsDirectory = is_directory(Status);
-  Data.IsNamedPipe = Status.type() == llvm::sys::fs::file_type::fifo_file;
+  Data.IsDirectory = Status.isDirectory();
+  Data.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
   Data.InPCH = false;
 }
 
@@ -50,22 +50,23 @@
 /// implementation can optionally fill in FileDescriptor with a valid
 /// descriptor and the client guarantees that it will close it.
 bool FileSystemStatCache::get(const char *Path, FileData &Data, bool isFile,
-                              int *FileDescriptor, FileSystemStatCache *Cache) {
+                              int *FileDescriptor, FileSystemStatCache *Cache,
+                              AbstractFileSystem &FS) {
   LookupResult R;
   bool isForDir = !isFile;
 
   // If we have a cache, use it to resolve the stat query.
   if (Cache)
-    R = Cache->getStat(Path, Data, isFile, FileDescriptor);
+    R = Cache->getStat(Path, Data, isFile, FileDescriptor, FS);
   else if (isForDir || !FileDescriptor) {
     // If this is a directory or a file descriptor is not needed and we have
     // no cache, just go to the file system.
-    llvm::sys::fs::file_status Status;
-    if (llvm::sys::fs::status(Path, Status)) {
+    llvm::ErrorOr<AbstractFileSystem::Status> Status = FS.status(Path);
+    if (!Status) {
       R = CacheMissing;
     } else {
       R = CacheExists;
-      copyStatusToFileData(Status, Data);
+      copyStatusToFileData(*Status, Data);
     }
   } else {
     // Otherwise, we have to go to the filesystem.  We can always just use
@@ -75,19 +76,21 @@
     //
     // Because of this, check to see if the file exists with 'open'.  If the
     // open succeeds, use fstat to get the stat info.
-    llvm::error_code EC = llvm::sys::fs::openFileForRead(Path, *FileDescriptor);
+    llvm::ErrorOr<int> FD = FS.openFileForRead(Path);
 
-    if (EC) {
+    if (!FD) {
       // If the open fails, our "stat" fails.
       R = CacheMissing;
     } else {
+      *FileDescriptor = *FD;
       // Otherwise, the open succeeded.  Do an fstat to get the information
       // about the file.  We'll end up returning the open file descriptor to the
       // client to do what they please with it.
-      llvm::sys::fs::file_status Status;
-      if (!llvm::sys::fs::status(*FileDescriptor, Status)) {
+      llvm::ErrorOr<AbstractFileSystem::Status> Status =
+        FS.status(*FileDescriptor);
+      if (Status) {
         R = CacheExists;
-        copyStatusToFileData(Status, Data);
+        copyStatusToFileData(*Status, Data);
       } else {
         // fstat rarely fails.  If it does, claim the initial open didn't
         // succeed.
@@ -118,8 +121,8 @@
 
 MemorizeStatCalls::LookupResult
 MemorizeStatCalls::getStat(const char *Path, FileData &Data, bool isFile,
-                           int *FileDescriptor) {
-  LookupResult Result = statChained(Path, Data, isFile, FileDescriptor);
+                           int *FileDescriptor, AbstractFileSystem &FS) {
+  LookupResult Result = statChained(Path, Data, isFile, FileDescriptor, FS);
 
   // Do not cache failed stats, it is easy to construct common inconsistent
   // situations if we do, and they are not important for PCH performance (which
Index: lib/Basic/VirtualFileSystem.cpp
===================================================================
--- /dev/null
+++ lib/Basic/VirtualFileSystem.cpp
@@ -0,0 +1,187 @@
+//===- VirtualFileSystem.cpp - Virtual File System Layer --------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This file implements the VirtualFileSystem interface.
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/VirtualFileSystem.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang;
+using namespace llvm;
+using llvm::sys::fs::file_status;
+using llvm::sys::fs::file_type;
+using llvm::sys::fs::perms;
+using llvm::sys::fs::UniqueID;
+
+AbstractFileSystem::Status::Status(const file_status &Status)
+    : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()),
+      User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()),
+      Type(Status.type()), Perms(Status.permissions()) {}
+
+AbstractFileSystem::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)
+    : Name(Name), ExternalName(ExternalName), UID(UID), MTime(MTime),
+      User(User), Group(Group), Size(Size), Type(Type), Perms(Perms) {}
+
+bool AbstractFileSystem::Status::equivalent(
+    const AbstractFileSystem::Status &Other) const {
+  return getUniqueID() == Other.getUniqueID();
+}
+bool AbstractFileSystem::Status::isDirectory() const {
+  return Type == file_type::directory_file;
+}
+bool AbstractFileSystem::Status::isRegularFile() const {
+  return Type == file_type::regular_file;
+}
+bool AbstractFileSystem::Status::isOther() const {
+  return Type == exists() && !isRegularFile() && !isDirectory() && !isSymlink();
+}
+bool AbstractFileSystem::Status::isSymlink() const {
+  return Type == file_type::symlink_file;
+}
+bool AbstractFileSystem::Status::isStatusKnown() const {
+  return Type != file_type::status_error;
+}
+bool AbstractFileSystem::Status::exists() const {
+  return isStatusKnown() && Type != file_type::file_not_found;
+}
+
+AbstractFileSystem::~AbstractFileSystem() {}
+
+ErrorOr<AbstractFileSystem::Status>
+AbstractFileSystem::status(AbstractFileSystem::FileDescriptor FD) {
+  // FIXME: when we support virtual files, use information from the FD to lookup
+  // which AbstractFileSystem to perform this operation on.
+  return getRealFileSystem()->statusOfOpenFile(FD);
+}
+
+error_code AbstractFileSystem::getBufferForFile(
+    FileDescriptor FD, const llvm::Twine &Name,
+    llvm::OwningPtr<llvm::MemoryBuffer> &Result, int64_t FileSize,
+    bool RequiresNullTerminator) {
+  // FIXME: when we support virtual files, use information from the FD to lookup
+  // which AbstractFileSystem to perform this operation on.
+  return getRealFileSystem()->getBufferForOpenFile(FD, Name, Result, FileSize,
+                                                   RequiresNullTerminator);
+}
+
+error_code AbstractFileSystem::close(AbstractFileSystem::FileDescriptor FD) {
+  // FIXME: when we support virtual files, use information from the FD to lookup
+  // which AbstractFileSystem to perform this operation on.
+  return getRealFileSystem()->closeOpenFile(FD);
+}
+
+error_code AbstractFileSystem::getBufferForFile(
+    const llvm::Twine &Name, llvm::OwningPtr<llvm::MemoryBuffer> &Result,
+    int64_t FileSize, bool RequiresNullTerminator) {
+  ErrorOr<FileDescriptor> FD = openFileForRead(Name);
+  if (!FD)
+    return FD.getError();
+
+  error_code EC = getBufferForFile(*FD, Name, Result, FileSize,
+                                   RequiresNullTerminator);
+  close(*FD);
+  return EC;
+}
+
+ErrorOr<AbstractFileSystem::Status>
+AbstractFileSystem::statusOfOpenFile(AbstractFileSystem::FileDescriptor FD) {
+  llvm_unreachable("status for open file not provided");
+}
+
+error_code AbstractFileSystem::getBufferForOpenFile(
+    FileDescriptor FD, const llvm::Twine &Name,
+    llvm::OwningPtr<llvm::MemoryBuffer> &Result, int64_t FileSize,
+    bool RequiresNullTerminator) {
+  llvm_unreachable("get buffer for open file not provided");
+}
+
+error_code
+AbstractFileSystem::closeOpenFile(AbstractFileSystem::FileDescriptor FD) {
+  llvm_unreachable("close open file not provided");
+}
+
+//===-----------------------------------------------------------------------===/
+// RealFileSystem implementation
+//===-----------------------------------------------------------------------===/
+
+/// \brief The file system according to your operating system.
+class RealFileSystem : public AbstractFileSystem {
+public:
+  ErrorOr<Status> status(const Twine &Path) LLVM_OVERRIDE;
+  ErrorOr<Status> statusOfOpenFile(FileDescriptor FD) LLVM_OVERRIDE;
+  ErrorOr<FileDescriptor> openFileForRead(const Twine &Path) LLVM_OVERRIDE;
+
+protected:
+  error_code
+  getBufferForOpenFile(FileDescriptor FD, const Twine &Name,
+                       OwningPtr<MemoryBuffer> &Result, int64_t FileSize = -1,
+                       bool RequiresNullTerminator = true) LLVM_OVERRIDE;
+  error_code closeOpenFile(FileDescriptor FD);
+};
+
+IntrusiveRefCntPtr<AbstractFileSystem> clang::getRealFileSystem() {
+  static IntrusiveRefCntPtr<AbstractFileSystem> FS = new RealFileSystem();
+  return FS;
+}
+
+ErrorOr<AbstractFileSystem::Status> RealFileSystem::status(const Twine &Path) {
+  sys::fs::file_status RealStatus;
+  if (error_code EC = sys::fs::status(Path, RealStatus))
+    return EC;
+  Status Result(RealStatus);
+  Result.setName(Path.str());
+  Result.setExternalName(Path.str());
+  return Result;
+}
+
+ErrorOr<AbstractFileSystem::Status>
+RealFileSystem::statusOfOpenFile(AbstractFileSystem::FileDescriptor FD) {
+  sys::fs::file_status RealStatus;
+  if (error_code EC = sys::fs::status(FD, RealStatus))
+    return EC;
+  return Status(RealStatus);
+}
+
+ErrorOr<AbstractFileSystem::FileDescriptor>
+RealFileSystem::openFileForRead(const Twine &Name) {
+  int ResultFD;
+  if (error_code EC = llvm::sys::fs::openFileForRead(Name, ResultFD))
+    return EC;
+  return ResultFD;
+}
+
+error_code RealFileSystem::getBufferForOpenFile(
+    AbstractFileSystem::FileDescriptor FD, const Twine &Name,
+    OwningPtr<MemoryBuffer> &Result, int64_t FileSize,
+    bool RequiresNullTerminator) {
+  return MemoryBuffer::getOpenFile(FD, Name.str().c_str(), Result, FileSize,
+                                   RequiresNullTerminator);
+}
+
+// FIXME: This is terrible, we need this for ::close.
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#include <sys/uio.h>
+#else
+#include <io.h>
+#ifndef S_ISFIFO
+#define S_ISFIFO(x) (0)
+#endif
+#endif
+error_code RealFileSystem::closeOpenFile(FileDescriptor FD) {
+  if (::close(FD))
+    return error_code(errno, system_category());
+  return error_code::success();
+}
Index: lib/Frontend/ASTUnit.cpp
===================================================================
--- lib/Frontend/ASTUnit.cpp
+++ lib/Frontend/ASTUnit.cpp
@@ -20,6 +20,7 @@
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/TargetOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
@@ -37,7 +38,6 @@
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/Atomic.h"
 #include "llvm/Support/CrashRecoveryContext.h"
-#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Host.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mutex.h"
@@ -1463,7 +1463,7 @@
              REnd = PreprocessorOpts.remapped_file_end();
            !AnyFileChanged && R != REnd;
            ++R) {
-        llvm::sys::fs::file_status Status;
+        AbstractFileSystem::Status Status;
         if (FileMgr->getNoncachedStatValue(R->second, Status)) {
           // If we can't stat the file we're remapping to, assume that something
           // horrible happened.
@@ -1499,7 +1499,7 @@
         }
         
         // The file was not remapped; check whether it has changed on disk.
-        llvm::sys::fs::file_status Status;
+        AbstractFileSystem::Status Status;
         if (FileMgr->getNoncachedStatValue(F->first(), Status)) {
           // If we can't stat the file, assume that something horrible happened.
           AnyFileChanged = true;
Index: lib/Frontend/CacheTokens.cpp
===================================================================
--- lib/Frontend/CacheTokens.cpp
+++ lib/Frontend/CacheTokens.cpp
@@ -516,8 +516,8 @@
   ~StatListener() {}
 
   LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                       int *FileDescriptor) {
-    LookupResult Result = statChained(Path, Data, isFile, FileDescriptor);
+                       int *FileDescriptor, AbstractFileSystem &FS) {
+    LookupResult Result = statChained(Path, Data, isFile, FileDescriptor, FS);
 
     if (Result == CacheMissing) // Failed 'stat'.
       PM.insert(PTHEntryKeyVariant(Path), PTHEntry());
Index: lib/Frontend/ChainedIncludesSource.cpp
===================================================================
--- lib/Frontend/ChainedIncludesSource.cpp
+++ lib/Frontend/ChainedIncludesSource.cpp
@@ -101,6 +101,7 @@
     Clang->setDiagnostics(Diags.getPtr());
     Clang->setTarget(TargetInfo::CreateTargetInfo(Clang->getDiagnostics(),
                                                   &Clang->getTargetOpts()));
+    Clang->createVirtualFileSystem();
     Clang->createFileManager();
     Clang->createSourceManager(Clang->getFileManager());
     Clang->createPreprocessor();
Index: lib/Frontend/CompilerInstance.cpp
===================================================================
--- lib/Frontend/CompilerInstance.cpp
+++ lib/Frontend/CompilerInstance.cpp
@@ -194,9 +194,14 @@
   return Diags;
 }
 
+void CompilerInstance::createVirtualFileSystem() {
+  VirtualFileSystem = getRealFileSystem();
+}
+
 // File Manager
 
 void CompilerInstance::createFileManager() {
+  assert(hasVirtualFileSystem() && "expected virtual file system");
   FileMgr = new FileManager(getFileSystemOpts());
 }
 
@@ -867,6 +872,8 @@
                                    ImportingInstance.getDiagnosticClient()),
                              /*ShouldOwnClient=*/true);
 
+  Instance.setVirtualFileSystem(&ImportingInstance.getVirtualFileSystem());
+
   // Note that this module is part of the module build stack, so that we
   // can detect cycles in the module graph.
   Instance.createFileManager(); // FIXME: Adopt file manager from importer?
Index: lib/Frontend/FrontendAction.cpp
===================================================================
--- lib/Frontend/FrontendAction.cpp
+++ lib/Frontend/FrontendAction.cpp
@@ -159,7 +159,6 @@
   return new MultiplexConsumer(Consumers);
 }
 
-
 bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
                                      const FrontendInputFile &Input) {
   assert(!Instance && "Already processing a source file!");
@@ -213,6 +212,8 @@
   }
 
   // Set up the file and source managers, if needed.
+  if (!CI.hasVirtualFileSystem())
+    CI.createVirtualFileSystem();
   if (!CI.hasFileManager())
     CI.createFileManager();
   if (!CI.hasSourceManager())
Index: lib/Lex/PTHLexer.cpp
===================================================================
--- lib/Lex/PTHLexer.cpp
+++ lib/Lex/PTHLexer.cpp
@@ -675,13 +675,13 @@
   ~PTHStatCache() {}
 
   LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                       int *FileDescriptor) {
+                       int *FileDescriptor, AbstractFileSystem &FS) {
     // Do the lookup for the file's data in the PTH file.
     CacheTy::iterator I = Cache.find(Path);
 
     // If we don't get a hit in the PTH file just forward to 'stat'.
     if (I == Cache.end())
-      return statChained(Path, Data, isFile, FileDescriptor);
+      return statChained(Path, Data, isFile, FileDescriptor, FS);
 
     const PTHStatData &D = *I;
 
Index: unittests/Basic/FileManagerTest.cpp
===================================================================
--- unittests/Basic/FileManagerTest.cpp
+++ unittests/Basic/FileManagerTest.cpp
@@ -48,7 +48,7 @@
 
   // Implement FileSystemStatCache::getStat().
   virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile,
-                               int *FileDescriptor) {
+                               int *FileDescriptor, AbstractFileSystem &FS) {
     if (StatCalls.count(Path) != 0) {
       Data = StatCalls[Path];
       return CacheExists;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to