1 //===--- FS.cpp - File system related utils ----------------------*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "FS.h" 10 #include "clang/Basic/LLVM.h" 11 #include "llvm/ADT/None.h" 12 #include "llvm/Support/Path.h" 13 14 namespace clang { 15 namespace clangd { 16 17 PreambleFileStatusCache::PreambleFileStatusCache(llvm::StringRef MainFilePath) 18 : MainFilePath(MainFilePath) { 19 assert(llvm::sys::path::is_absolute(MainFilePath)); 20 } 21 22 void PreambleFileStatusCache::update(const llvm::vfs::FileSystem &FS, 23 llvm::vfs::Status S) { 24 llvm::SmallString<32> PathStore(S.getName()); 25 if (FS.makeAbsolute(PathStore)) 26 return; 27 // Do not cache status for the main file. 28 if (PathStore == MainFilePath) 29 return; 30 // Stores the latest status in cache as it can change in a preamble build. 31 StatCache.insert({PathStore, std::move(S)}); 32 } 33 34 llvm::Optional<llvm::vfs::Status> 35 PreambleFileStatusCache::lookup(llvm::StringRef File) const { 36 auto I = StatCache.find(File); 37 if (I != StatCache.end()) 38 return I->getValue(); 39 return None; 40 } 41 42 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 43 PreambleFileStatusCache::getProducingFS( 44 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) { 45 // This invalidates old status in cache if files are re-`open()`ed or 46 // re-`stat()`ed in case file status has changed during preamble build. 47 class CollectFS : public llvm::vfs::ProxyFileSystem { 48 public: 49 CollectFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, 50 PreambleFileStatusCache &StatCache) 51 : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} 52 53 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 54 openFileForRead(const llvm::Twine &Path) override { 55 auto File = getUnderlyingFS().openFileForRead(Path); 56 if (!File || !*File) 57 return File; 58 // Eagerly stat opened file, as the followup `status` call on the file 59 // doesn't necessarily go through this FS. This puts some extra work on 60 // preamble build, but it should be worth it as preamble can be reused 61 // many times (e.g. code completion) and the repeated status call is 62 // likely to be cached in the underlying file system anyway. 63 if (auto S = File->get()->status()) 64 StatCache.update(getUnderlyingFS(), std::move(*S)); 65 return File; 66 } 67 68 llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override { 69 auto S = getUnderlyingFS().status(Path); 70 if (S) 71 StatCache.update(getUnderlyingFS(), *S); 72 return S; 73 } 74 75 private: 76 PreambleFileStatusCache &StatCache; 77 }; 78 return llvm::IntrusiveRefCntPtr<CollectFS>( 79 new CollectFS(std::move(FS), *this)); 80 } 81 82 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> 83 PreambleFileStatusCache::getConsumingFS( 84 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) const { 85 class CacheVFS : public llvm::vfs::ProxyFileSystem { 86 public: 87 CacheVFS(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, 88 const PreambleFileStatusCache &StatCache) 89 : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} 90 91 llvm::ErrorOr<llvm::vfs::Status> status(const llvm::Twine &Path) override { 92 if (auto S = StatCache.lookup(Path.str())) 93 return *S; 94 return getUnderlyingFS().status(Path); 95 } 96 97 private: 98 const PreambleFileStatusCache &StatCache; 99 }; 100 return llvm::IntrusiveRefCntPtr<CacheVFS>(new CacheVFS(std::move(FS), *this)); 101 } 102 103 } // namespace clangd 104 } // namespace clang 105