llvm-project/clang/lib/Basic/FileSystemStatCache.cpp
Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

130 lines
4.7 KiB
C++

//===- FileSystemStatCache.cpp - Caching for 'stat' calls -----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the FileSystemStatCache interface.
//
//===----------------------------------------------------------------------===//
#include "clang/Basic/FileSystemStatCache.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
#include <utility>
using namespace clang;
void FileSystemStatCache::anchor() {}
static void copyStatusToFileData(const llvm::vfs::Status &Status,
FileData &Data) {
Data.Name = Status.getName();
Data.Size = Status.getSize();
Data.ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
Data.UniqueID = Status.getUniqueID();
Data.IsDirectory = Status.isDirectory();
Data.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
Data.InPCH = false;
Data.IsVFSMapped = Status.IsVFSMapped;
}
/// FileSystemStatCache::get - Get the 'stat' information for the specified
/// path, using the cache to accelerate it if possible. This returns true if
/// the path does not exist or false if it exists.
///
/// If isFile is true, then this lookup should only return success for files
/// (not directories). If it is false this lookup should only return
/// success for directories (not files). On a successful file lookup, the
/// implementation can optionally fill in FileDescriptor with a valid
/// descriptor and the client guarantees that it will close it.
bool FileSystemStatCache::get(StringRef Path, FileData &Data, bool isFile,
std::unique_ptr<llvm::vfs::File> *F,
FileSystemStatCache *Cache,
llvm::vfs::FileSystem &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, F, FS);
else if (isForDir || !F) {
// If this is a directory or a file descriptor is not needed and we have
// no cache, just go to the file system.
llvm::ErrorOr<llvm::vfs::Status> Status = FS.status(Path);
if (!Status) {
R = CacheMissing;
} else {
R = CacheExists;
copyStatusToFileData(*Status, Data);
}
} else {
// Otherwise, we have to go to the filesystem. We can always just use
// 'stat' here, but (for files) the client is asking whether the file exists
// because it wants to turn around and *open* it. It is more efficient to
// do "open+fstat" on success than it is to do "stat+open".
//
// Because of this, check to see if the file exists with 'open'. If the
// open succeeds, use fstat to get the stat info.
auto OwnedFile = FS.openFileForRead(Path);
if (!OwnedFile) {
// If the open fails, our "stat" fails.
R = CacheMissing;
} else {
// 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::ErrorOr<llvm::vfs::Status> Status = (*OwnedFile)->status();
if (Status) {
R = CacheExists;
copyStatusToFileData(*Status, Data);
*F = std::move(*OwnedFile);
} else {
// fstat rarely fails. If it does, claim the initial open didn't
// succeed.
R = CacheMissing;
*F = nullptr;
}
}
}
// If the path doesn't exist, return failure.
if (R == CacheMissing) return true;
// If the path exists, make sure that its "directoryness" matches the clients
// demands.
if (Data.IsDirectory != isForDir) {
// If not, close the file if opened.
if (F)
*F = nullptr;
return true;
}
return false;
}
MemorizeStatCalls::LookupResult
MemorizeStatCalls::getStat(StringRef Path, FileData &Data, bool isFile,
std::unique_ptr<llvm::vfs::File> *F,
llvm::vfs::FileSystem &FS) {
if (get(Path, Data, isFile, F, nullptr, 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 currently only needs the stats to construct the initial
// FileManager entries).
return CacheMissing;
}
// Cache file 'stat' results and directories with absolutely paths.
if (!Data.IsDirectory || llvm::sys::path::is_absolute(Path))
StatCalls[Path] = Data;
return CacheExists;
}