1 | //===- clang/Basic/FileEntry.h - File references ----------------*- 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 | /// \file |
10 | /// Defines interfaces for clang::FileEntry and clang::FileEntryRef. |
11 | /// |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_CLANG_BASIC_FILEENTRY_H |
15 | #define LLVM_CLANG_BASIC_FILEENTRY_H |
16 | |
17 | #include "clang/Basic/CustomizableOptional.h" |
18 | #include "clang/Basic/DirectoryEntry.h" |
19 | #include "clang/Basic/LLVM.h" |
20 | #include "llvm/ADT/DenseMapInfo.h" |
21 | #include "llvm/ADT/Hashing.h" |
22 | #include "llvm/ADT/PointerUnion.h" |
23 | #include "llvm/ADT/StringMap.h" |
24 | #include "llvm/ADT/StringRef.h" |
25 | #include "llvm/Support/ErrorOr.h" |
26 | #include "llvm/Support/FileSystem/UniqueID.h" |
27 | |
28 | #include <optional> |
29 | #include <utility> |
30 | |
31 | namespace llvm { |
32 | |
33 | class MemoryBuffer; |
34 | |
35 | namespace vfs { |
36 | |
37 | class File; |
38 | |
39 | } // namespace vfs |
40 | } // namespace llvm |
41 | |
42 | namespace clang { |
43 | |
44 | class FileEntryRef; |
45 | |
46 | namespace optional_detail { |
47 | |
48 | /// Forward declare a template specialization for OptionalStorage. |
49 | template <> class OptionalStorage<clang::FileEntryRef>; |
50 | |
51 | } // namespace optional_detail |
52 | |
53 | class FileEntry; |
54 | |
55 | /// A reference to a \c FileEntry that includes the name of the file as it was |
56 | /// accessed by the FileManager's client. |
57 | class FileEntryRef { |
58 | public: |
59 | /// The name of this FileEntry. If a VFS uses 'use-external-name', this is |
60 | /// the redirected name. See getRequestedName(). |
61 | StringRef getName() const { return getBaseMapEntry().first(); } |
62 | |
63 | /// The name of this FileEntry, as originally requested without applying any |
64 | /// remappings for VFS 'use-external-name'. |
65 | /// |
66 | /// FIXME: this should be the semantics of getName(). See comment in |
67 | /// FileManager::getFileRef(). |
68 | StringRef getNameAsRequested() const { return ME->first(); } |
69 | |
70 | const FileEntry &getFileEntry() const { |
71 | return *cast<FileEntry *>(Val: getBaseMapEntry().second->V); |
72 | } |
73 | |
74 | // This function is used if the buffer size needs to be increased |
75 | // due to potential z/OS EBCDIC -> UTF-8 conversion |
76 | inline void updateFileEntryBufferSize(unsigned BufferSize); |
77 | |
78 | DirectoryEntryRef getDir() const { return ME->second->Dir; } |
79 | |
80 | inline off_t getSize() const; |
81 | inline unsigned getUID() const; |
82 | inline const llvm::sys::fs::UniqueID &getUniqueID() const; |
83 | inline time_t getModificationTime() const; |
84 | inline bool isNamedPipe() const; |
85 | inline bool isDeviceFile() const; |
86 | inline void closeFile() const; |
87 | |
88 | /// Check if the underlying FileEntry is the same, intentially ignoring |
89 | /// whether the file was referenced with the same spelling of the filename. |
90 | friend bool operator==(const FileEntryRef &LHS, const FileEntryRef &RHS) { |
91 | return &LHS.getFileEntry() == &RHS.getFileEntry(); |
92 | } |
93 | friend bool operator==(const FileEntry *LHS, const FileEntryRef &RHS) { |
94 | return LHS == &RHS.getFileEntry(); |
95 | } |
96 | friend bool operator==(const FileEntryRef &LHS, const FileEntry *RHS) { |
97 | return &LHS.getFileEntry() == RHS; |
98 | } |
99 | friend bool operator!=(const FileEntryRef &LHS, const FileEntryRef &RHS) { |
100 | return !(LHS == RHS); |
101 | } |
102 | friend bool operator!=(const FileEntry *LHS, const FileEntryRef &RHS) { |
103 | return !(LHS == RHS); |
104 | } |
105 | friend bool operator!=(const FileEntryRef &LHS, const FileEntry *RHS) { |
106 | return !(LHS == RHS); |
107 | } |
108 | |
109 | /// Hash code is based on the FileEntry, not the specific named reference, |
110 | /// just like operator==. |
111 | friend llvm::hash_code hash_value(FileEntryRef Ref) { |
112 | return llvm::hash_value(ptr: &Ref.getFileEntry()); |
113 | } |
114 | |
115 | struct MapValue; |
116 | |
117 | /// Type used in the StringMap. |
118 | using MapEntry = llvm::StringMapEntry<llvm::ErrorOr<MapValue>>; |
119 | |
120 | /// Type stored in the StringMap. |
121 | struct MapValue { |
122 | /// The pointer at another MapEntry is used when the FileManager should |
123 | /// silently forward from one name to another, which occurs in Redirecting |
124 | /// VFSs that use external names. In that case, the \c FileEntryRef |
125 | /// returned by the \c FileManager will have the external name, and not the |
126 | /// name that was used to lookup the file. |
127 | llvm::PointerUnion<FileEntry *, const MapEntry *> V; |
128 | |
129 | /// Directory the file was found in. |
130 | DirectoryEntryRef Dir; |
131 | |
132 | MapValue() = delete; |
133 | MapValue(FileEntry &FE, DirectoryEntryRef Dir) : V(&FE), Dir(Dir) {} |
134 | MapValue(MapEntry &ME, DirectoryEntryRef Dir) : V(&ME), Dir(Dir) {} |
135 | }; |
136 | |
137 | /// Check if RHS referenced the file in exactly the same way. |
138 | bool isSameRef(const FileEntryRef &RHS) const { return ME == RHS.ME; } |
139 | |
140 | /// Allow FileEntryRef to degrade into 'const FileEntry*' to facilitate |
141 | /// incremental adoption. |
142 | /// |
143 | /// The goal is to avoid code churn due to dances like the following: |
144 | /// \code |
145 | /// // Old code. |
146 | /// lvalue = rvalue; |
147 | /// |
148 | /// // Temporary code from an incremental patch. |
149 | /// lvalue = &rvalue.getFileEntry(); |
150 | /// |
151 | /// // Final code. |
152 | /// lvalue = rvalue; |
153 | /// \endcode |
154 | /// |
155 | /// FIXME: Once FileEntryRef is "everywhere" and FileEntry::LastRef and |
156 | /// FileEntry::getName have been deleted, delete this implicit conversion. |
157 | operator const FileEntry *() const { return &getFileEntry(); } |
158 | |
159 | FileEntryRef() = delete; |
160 | explicit FileEntryRef(const MapEntry &ME) : ME(&ME) { |
161 | assert(ME.second && "Expected payload"); |
162 | assert(ME.second->V && "Expected non-null"); |
163 | } |
164 | |
165 | /// Expose the underlying MapEntry to simplify packing in a PointerIntPair or |
166 | /// PointerUnion and allow construction in Optional. |
167 | const clang::FileEntryRef::MapEntry &getMapEntry() const { return *ME; } |
168 | |
169 | /// Retrieve the base MapEntry after redirects. |
170 | const MapEntry &getBaseMapEntry() const { |
171 | const MapEntry *Base = ME; |
172 | while (const auto *Next = Base->second->V.dyn_cast<const MapEntry *>()) |
173 | Base = Next; |
174 | return *Base; |
175 | } |
176 | |
177 | private: |
178 | friend class FileMgr::MapEntryOptionalStorage<FileEntryRef>; |
179 | struct optional_none_tag {}; |
180 | |
181 | // Private constructor for use by OptionalStorage. |
182 | FileEntryRef(optional_none_tag) : ME(nullptr) {} |
183 | bool hasOptionalValue() const { return ME; } |
184 | |
185 | friend struct llvm::DenseMapInfo<FileEntryRef>; |
186 | struct dense_map_empty_tag {}; |
187 | struct dense_map_tombstone_tag {}; |
188 | |
189 | // Private constructors for use by DenseMapInfo. |
190 | FileEntryRef(dense_map_empty_tag) |
191 | : ME(llvm::DenseMapInfo<const MapEntry *>::getEmptyKey()) {} |
192 | FileEntryRef(dense_map_tombstone_tag) |
193 | : ME(llvm::DenseMapInfo<const MapEntry *>::getTombstoneKey()) {} |
194 | bool isSpecialDenseMapKey() const { |
195 | return isSameRef(RHS: FileEntryRef(dense_map_empty_tag())) || |
196 | isSameRef(RHS: FileEntryRef(dense_map_tombstone_tag())); |
197 | } |
198 | |
199 | const MapEntry *ME; |
200 | }; |
201 | |
202 | static_assert(sizeof(FileEntryRef) == sizeof(const FileEntry *), |
203 | "FileEntryRef must avoid size overhead"); |
204 | |
205 | static_assert(std::is_trivially_copyable<FileEntryRef>::value, |
206 | "FileEntryRef must be trivially copyable"); |
207 | |
208 | using OptionalFileEntryRef = CustomizableOptional<FileEntryRef>; |
209 | |
210 | namespace optional_detail { |
211 | |
212 | /// Customize OptionalStorage<FileEntryRef> to use FileEntryRef and its |
213 | /// optional_none_tag to keep it the size of a single pointer. |
214 | template <> |
215 | class OptionalStorage<clang::FileEntryRef> |
216 | : public clang::FileMgr::MapEntryOptionalStorage<clang::FileEntryRef> { |
217 | using StorageImpl = |
218 | clang::FileMgr::MapEntryOptionalStorage<clang::FileEntryRef>; |
219 | |
220 | public: |
221 | OptionalStorage() = default; |
222 | |
223 | template <class... ArgTypes> |
224 | explicit OptionalStorage(std::in_place_t, ArgTypes &&...Args) |
225 | : StorageImpl(std::in_place_t{}, std::forward<ArgTypes>(Args)...) {} |
226 | |
227 | OptionalStorage &operator=(clang::FileEntryRef Ref) { |
228 | StorageImpl::operator=(Ref); |
229 | return *this; |
230 | } |
231 | }; |
232 | |
233 | static_assert(sizeof(OptionalFileEntryRef) == sizeof(FileEntryRef), |
234 | "OptionalFileEntryRef must avoid size overhead"); |
235 | |
236 | static_assert(std::is_trivially_copyable<OptionalFileEntryRef>::value, |
237 | "OptionalFileEntryRef should be trivially copyable"); |
238 | |
239 | } // end namespace optional_detail |
240 | } // namespace clang |
241 | |
242 | namespace llvm { |
243 | |
244 | /// Specialisation of DenseMapInfo for FileEntryRef. |
245 | template <> struct DenseMapInfo<clang::FileEntryRef> { |
246 | static inline clang::FileEntryRef getEmptyKey() { |
247 | return clang::FileEntryRef(clang::FileEntryRef::dense_map_empty_tag()); |
248 | } |
249 | |
250 | static inline clang::FileEntryRef getTombstoneKey() { |
251 | return clang::FileEntryRef(clang::FileEntryRef::dense_map_tombstone_tag()); |
252 | } |
253 | |
254 | static unsigned getHashValue(clang::FileEntryRef Val) { |
255 | return hash_value(Ref: Val); |
256 | } |
257 | |
258 | static bool isEqual(clang::FileEntryRef LHS, clang::FileEntryRef RHS) { |
259 | // Catch the easy cases: both empty, both tombstone, or the same ref. |
260 | if (LHS.isSameRef(RHS)) |
261 | return true; |
262 | |
263 | // Confirm LHS and RHS are valid. |
264 | if (LHS.isSpecialDenseMapKey() || RHS.isSpecialDenseMapKey()) |
265 | return false; |
266 | |
267 | // It's safe to use operator==. |
268 | return LHS == RHS; |
269 | } |
270 | |
271 | /// Support for finding `const FileEntry *` in a `DenseMap<FileEntryRef, T>`. |
272 | /// @{ |
273 | static unsigned getHashValue(const clang::FileEntry *Val) { |
274 | return llvm::hash_value(ptr: Val); |
275 | } |
276 | static bool isEqual(const clang::FileEntry *LHS, clang::FileEntryRef RHS) { |
277 | if (RHS.isSpecialDenseMapKey()) |
278 | return false; |
279 | return LHS == RHS; |
280 | } |
281 | /// @} |
282 | }; |
283 | |
284 | } // end namespace llvm |
285 | |
286 | namespace clang { |
287 | |
288 | inline bool operator==(const FileEntry *LHS, const OptionalFileEntryRef &RHS) { |
289 | return LHS == (RHS ? &RHS->getFileEntry() : nullptr); |
290 | } |
291 | inline bool operator==(const OptionalFileEntryRef &LHS, const FileEntry *RHS) { |
292 | return (LHS ? &LHS->getFileEntry() : nullptr) == RHS; |
293 | } |
294 | inline bool operator!=(const FileEntry *LHS, const OptionalFileEntryRef &RHS) { |
295 | return !(LHS == RHS); |
296 | } |
297 | inline bool operator!=(const OptionalFileEntryRef &LHS, const FileEntry *RHS) { |
298 | return !(LHS == RHS); |
299 | } |
300 | |
301 | /// Cached information about one file (either on disk |
302 | /// or in the virtual file system). |
303 | /// |
304 | /// If the 'File' member is valid, then this FileEntry has an open file |
305 | /// descriptor for the file. |
306 | class FileEntry { |
307 | friend class FileManager; |
308 | friend class FileEntryTestHelper; |
309 | FileEntry(); |
310 | FileEntry(const FileEntry &) = delete; |
311 | FileEntry &operator=(const FileEntry &) = delete; |
312 | |
313 | std::string RealPathName; // Real path to the file; could be empty. |
314 | off_t Size = 0; // File size in bytes. |
315 | time_t ModTime = 0; // Modification time of file. |
316 | const DirectoryEntry *Dir = nullptr; // Directory file lives in. |
317 | llvm::sys::fs::UniqueID UniqueID; |
318 | unsigned UID = 0; // A unique (small) ID for the file. |
319 | bool IsNamedPipe = false; |
320 | bool IsDeviceFile = false; |
321 | |
322 | /// The open file, if it is owned by the \p FileEntry. |
323 | mutable std::unique_ptr<llvm::vfs::File> File; |
324 | |
325 | /// The file content, if it is owned by the \p FileEntry. |
326 | std::unique_ptr<llvm::MemoryBuffer> Content; |
327 | |
328 | public: |
329 | ~FileEntry(); |
330 | |
331 | StringRef tryGetRealPathName() const { return RealPathName; } |
332 | off_t getSize() const { return Size; } |
333 | // Size may increase due to potential z/OS EBCDIC -> UTF-8 conversion. |
334 | void setSize(off_t NewSize) { Size = NewSize; } |
335 | unsigned getUID() const { return UID; } |
336 | const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; } |
337 | time_t getModificationTime() const { return ModTime; } |
338 | |
339 | /// Return the directory the file lives in. |
340 | const DirectoryEntry *getDir() const { return Dir; } |
341 | |
342 | /// Check whether the file is a named pipe (and thus can't be opened by |
343 | /// the native FileManager methods). |
344 | bool isNamedPipe() const { return IsNamedPipe; } |
345 | bool isDeviceFile() const { return IsDeviceFile; } |
346 | |
347 | void closeFile() const; |
348 | }; |
349 | |
350 | off_t FileEntryRef::getSize() const { return getFileEntry().getSize(); } |
351 | |
352 | unsigned FileEntryRef::getUID() const { return getFileEntry().getUID(); } |
353 | |
354 | const llvm::sys::fs::UniqueID &FileEntryRef::getUniqueID() const { |
355 | return getFileEntry().getUniqueID(); |
356 | } |
357 | |
358 | time_t FileEntryRef::getModificationTime() const { |
359 | return getFileEntry().getModificationTime(); |
360 | } |
361 | |
362 | bool FileEntryRef::isNamedPipe() const { return getFileEntry().isNamedPipe(); } |
363 | bool FileEntryRef::isDeviceFile() const { |
364 | return getFileEntry().isDeviceFile(); |
365 | } |
366 | |
367 | void FileEntryRef::closeFile() const { getFileEntry().closeFile(); } |
368 | |
369 | void FileEntryRef::updateFileEntryBufferSize(unsigned BufferSize) { |
370 | cast<FileEntry *>(Val: getBaseMapEntry().second->V)->setSize(BufferSize); |
371 | } |
372 | |
373 | } // end namespace clang |
374 | |
375 | #endif // LLVM_CLANG_BASIC_FILEENTRY_H |
376 |
Definitions
- FileEntryRef
- getName
- getNameAsRequested
- getFileEntry
- getDir
- operator==
- operator==
- operator==
- operator!=
- operator!=
- operator!=
- hash_value
- MapValue
- MapValue
- MapValue
- MapValue
- isSameRef
- operator const FileEntry *
- FileEntryRef
- FileEntryRef
- getMapEntry
- getBaseMapEntry
- optional_none_tag
- FileEntryRef
- hasOptionalValue
- dense_map_empty_tag
- dense_map_tombstone_tag
- FileEntryRef
- FileEntryRef
- isSpecialDenseMapKey
- OptionalStorage
- OptionalStorage
- OptionalStorage
- operator=
- DenseMapInfo
- getEmptyKey
- getTombstoneKey
- getHashValue
- isEqual
- getHashValue
- isEqual
- operator==
- operator==
- operator!=
- operator!=
- FileEntry
- FileEntry
- operator=
- tryGetRealPathName
- getSize
- setSize
- getUID
- getUniqueID
- getModificationTime
- getDir
- isNamedPipe
- isDeviceFile
- getSize
- getUID
- getUniqueID
- getModificationTime
- isNamedPipe
- isDeviceFile
- closeFile
Improve your Profiling and Debugging skills
Find out more