sync the repo
[hiphop-php.git] / hphp / runtime / ext / facts / symbol-map.h
blob3c26119cd2bf97fc22d8d58a2cc71aa586dc58b9
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source path is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the path LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #pragma once
19 #include <atomic>
20 #include <filesystem>
21 #include <memory>
22 #include <mutex>
23 #include <queue>
24 #include <set>
25 #include <string>
26 #include <thread>
27 #include <vector>
29 #include <folly/Executor.h>
30 #include <folly/SharedMutex.h>
31 #include <folly/Synchronized.h>
32 #include <folly/futures/Future.h>
33 #include <folly/futures/FutureSplitter.h>
34 #include "hphp/runtime/ext/facts/attribute-map.h"
35 #include "hphp/runtime/ext/facts/autoload-db.h"
36 #include "hphp/runtime/ext/facts/file-facts.h"
37 #include "hphp/runtime/ext/facts/inheritance-info.h"
38 #include "hphp/runtime/ext/facts/lazy-two-way-map.h"
39 #include "hphp/runtime/ext/facts/path-symbols-map.h"
40 #include "hphp/runtime/ext/facts/path-versions.h"
41 #include "hphp/runtime/ext/facts/string-ptr.h"
42 #include "hphp/runtime/ext/facts/symbol-types.h"
43 #include "hphp/util/assertions.h"
44 #include "hphp/util/hash-map.h"
45 #include "hphp/util/hash-set.h"
46 #include "hphp/util/sha1.h"
47 #include "hphp/util/sqlite-wrapper.h"
49 namespace HPHP {
51 struct StringData;
53 namespace Facts {
55 struct UpdateDBWorkItem {
56 Clock m_since;
57 Clock m_clock;
58 std::vector<std::filesystem::path> m_alteredPaths;
59 std::vector<std::filesystem::path> m_deletedPaths;
60 std::vector<FileFacts> m_alteredPathFacts;
63 /**
64 * Stores a map from thread to AutoloadDB.
66 struct AutoloadDBVault {
67 explicit AutoloadDBVault(AutoloadDB::Opener);
69 /**
70 * Get the AutoloadDB associated with the thread that calls this method.
72 * Logically this is `const`, but it creates an AutoloadDB on first access.
74 std::shared_ptr<AutoloadDB> get() const;
76 private:
77 AutoloadDB::Opener m_dbOpener;
78 // Holds one AutoloadDB per thread. Creates an AutoloadDB on the first access.
79 mutable folly::Synchronized<
80 hphp_hash_map<std::thread::id, std::shared_ptr<AutoloadDB>>>
81 m_dbs;
84 /**
85 * Stores and updates one PathToSymbolsMap for each kind of symbol.
87 * Multiple readers can run concurrently with one writer, but only one
88 * call to SymbolMap::update should happen at any given time.
90 * Queries the SQLite AutoloadDB when the DB has information
91 * we don't, and updates the AutoloadDB when we have
92 * information the DB doesn't have.
94 struct SymbolMap {
95 explicit SymbolMap(
96 std::filesystem::path root,
97 AutoloadDB::Opener dbOpener,
98 hphp_vector_set<Symbol<SymKind::Type>> indexedMethodAttributes,
99 bool enableBlockingDbWait,
100 bool useSymbolMapForGetFilesWithAttrAndAnyVal,
101 std::chrono::milliseconds blockingDbWaitTimeout);
102 SymbolMap() = delete;
103 SymbolMap(const SymbolMap&) = delete;
104 SymbolMap(SymbolMap&&) noexcept = delete;
105 SymbolMap& operator=(const SymbolMap&) = delete;
106 SymbolMap& operator=(SymbolMap&&) noexcept = delete;
107 ~SymbolMap();
110 * Resolve a type's name to its canonical, correctly-capitalized name.
112 * Return nullptr if the type is not defined, or if the type is defined in
113 * more than one file.
115 Optional<Symbol<SymKind::Type>> getTypeName(const StringData& type);
118 * Return the one and only definition for the given symbol.
120 * If the symbol is defined in no files, or in more than one file,
121 * return nullptr.
123 * These methods may fill the map with information from the SQLite
124 * DB, and as such may throw SQLite exceptions.
126 Path getTypeOrTypeAliasFile(Symbol<SymKind::Type> type);
127 Path getTypeOrTypeAliasFile(const StringData& type);
128 Path getTypeFile(Symbol<SymKind::Type> type);
129 Path getTypeFile(const StringData& type);
130 Path getFunctionFile(Symbol<SymKind::Function> function);
131 Path getFunctionFile(const StringData& function);
132 Path getConstantFile(Symbol<SymKind::Constant> constant);
133 Path getConstantFile(const StringData& constant);
134 // Returns the file containing the module definition.
135 Path getModuleFile(Symbol<SymKind::Module> module);
136 // Returns the file containing the module definition.
137 Path getModuleFile(const StringData& module);
138 Path getTypeAliasFile(Symbol<SymKind::Type> typeAlias);
139 Path getTypeAliasFile(const StringData& typeAlias);
142 * Return all symbols of a given kind declared in the given path.
144 * These methods may fill the map with information from the SQLite
145 * DB, and as such may throw SQLite exceptions.
147 std::vector<Symbol<SymKind::Type>> getFileTypes(Path path);
148 std::vector<Symbol<SymKind::Type>> getFileTypes(const std::filesystem::path&);
150 std::vector<Symbol<SymKind::Function>> getFileFunctions(Path path);
151 std::vector<Symbol<SymKind::Function>> getFileFunctions(
152 const std::filesystem::path& path);
154 std::vector<Symbol<SymKind::Constant>> getFileConstants(Path path);
155 std::vector<Symbol<SymKind::Constant>> getFileConstants(
156 const std::filesystem::path& path);
158 // Returns the set of Modules defined in the given file.
159 std::vector<Symbol<SymKind::Module>> getFileModules(Path path);
160 std::vector<Symbol<SymKind::Module>> getFileModules(
161 const std::filesystem::path& path);
163 // Returns the module the file is contained in, if any.
164 std::optional<Symbol<SymKind::ModuleMembership>> getFileModuleMembership(
165 Path path);
166 std::optional<Symbol<SymKind::ModuleMembership>> getFileModuleMembership(
167 const std::filesystem::path& path);
169 std::vector<Symbol<SymKind::Type>> getFileTypeAliases(Path path);
170 std::vector<Symbol<SymKind::Type>> getFileTypeAliases(
171 const std::filesystem::path& path);
174 * Return inheritance data about the given type
176 std::vector<Symbol<SymKind::Type>> getBaseTypes(
177 Symbol<SymKind::Type> derivedType,
178 DeriveKind kind);
179 std::vector<Symbol<SymKind::Type>> getBaseTypes(
180 const StringData& derivedType,
181 DeriveKind kind);
183 std::vector<Symbol<SymKind::Type>> getDerivedTypes(
184 Symbol<SymKind::Type> baseType,
185 DeriveKind kind);
186 std::vector<Symbol<SymKind::Type>> getDerivedTypes(
187 const StringData& baseType,
188 DeriveKind kind);
191 * Return the attributes of a type
193 std::vector<Symbol<SymKind::Type>> getAttributesOfType(
194 Symbol<SymKind::Type> type);
195 std::vector<Symbol<SymKind::Type>> getAttributesOfType(
196 const StringData& type);
199 * Return the attributes decorating a type alias
201 std::vector<Symbol<SymKind::Type>> getAttributesOfTypeAlias(
202 Symbol<SymKind::Type> typeAlias);
203 std::vector<Symbol<SymKind::Type>> getAttributesOfTypeAlias(
204 const StringData& typeAlias);
207 * Return the types decorated with a given attribute
209 std::vector<Symbol<SymKind::Type>> getTypesWithAttribute(
210 Symbol<SymKind::Type> attr);
211 std::vector<Symbol<SymKind::Type>> getTypesWithAttribute(
212 const StringData& attr);
215 * Return the type aliases decorated with a given attribute
217 std::vector<Symbol<SymKind::Type>> getTypeAliasesWithAttribute(
218 Symbol<SymKind::Type> attr);
219 std::vector<Symbol<SymKind::Type>> getTypeAliasesWithAttribute(
220 const StringData& attr);
223 * Return the attributes of a method
225 std::vector<Symbol<SymKind::Type>> getAttributesOfMethod(
226 Symbol<SymKind::Type> type,
227 Symbol<SymKind::Method> method);
228 std::vector<Symbol<SymKind::Type>> getAttributesOfMethod(
229 const StringData& type,
230 const StringData& method);
233 * Return the methods with a given attribute
235 std::vector<MethodDecl> getMethodsWithAttribute(Symbol<SymKind::Type> attr);
236 std::vector<MethodDecl> getMethodsWithAttribute(const StringData& attr);
239 * Return the attributes of a file
241 std::vector<Symbol<SymKind::Type>> getAttributesOfFile(Path path);
244 * Return the files with a given attribute
246 std::vector<Path> getFilesWithAttribute(Symbol<SymKind::Type> attr);
247 std::vector<Path> getFilesWithAttribute(const StringData& attr);
250 * Return the files with a given attribute and value
252 std::vector<Path> getFilesWithAttributeAndAnyValue(
253 Symbol<SymKind::Type> attr,
254 const folly::dynamic& value);
255 std::vector<Path> getFilesWithAttributeAndAnyValue(
256 const StringData& attr,
257 const folly::dynamic& value);
259 std::vector<FileAttrVal> getFilesAndAttrValsWithAttribute(
260 Symbol<SymKind::Type> attr);
262 std::vector<FileAttrVal> getFilesAndAttrValsWithAttribute(
263 const StringData& attr);
266 * Return the argument at the given position of a given type with a given
267 * attribute.
269 * So if a type were defined with an attribute like this:
271 * <<Oncalls('hhvm')>>
272 * class Foo {}
274 * You'd expect to be able to extract that "hhvm" argument this way:
276 * getAttributeArg("Foo", "Oncalls").at(0) == "hhvm"
278 * If this function returns an empty vector, it could be because:
280 * - The type is not defined.
281 * - The type is defined in more than one file, violating the One Definition
282 * Rule.
283 * - The type doesn't have the given attribute.
284 * - The attribute doesn't have any arguments.
286 * You can check that the type is defined with `getTypeFile()`, and you can
287 * check that the type has the given attribute with `getAttributesOfType()`.
289 std::vector<folly::dynamic> getTypeAttributeArgs(
290 Symbol<SymKind::Type> type,
291 Symbol<SymKind::Type> attribute);
292 std::vector<folly::dynamic> getTypeAttributeArgs(
293 const StringData& type,
294 const StringData& attribute);
296 std::vector<folly::dynamic> getTypeAliasAttributeArgs(
297 Symbol<SymKind::Type> type,
298 Symbol<SymKind::Type> attribute);
299 std::vector<folly::dynamic> getTypeAliasAttributeArgs(
300 const StringData& type,
301 const StringData& attribute);
303 std::vector<folly::dynamic> getMethodAttributeArgs(
304 Symbol<SymKind::Type> type,
305 Symbol<SymKind::Method> method,
306 Symbol<SymKind::Type> attribute);
307 std::vector<folly::dynamic> getMethodAttributeArgs(
308 const StringData& type,
309 const StringData& method,
310 const StringData& attribute);
312 std::vector<folly::dynamic> getFileAttributeArgs(
313 Path path,
314 Symbol<SymKind::Type> attribute);
315 std::vector<folly::dynamic> getFileAttributeArgs(
316 Path path,
317 const StringData& attribute);
320 * Return whether the given type is, for example, a class or interface.
322 * Return `TypeKind::Unknown` if the given type does not have a unique
323 * definition or is not an autoloadable type at all.
325 TypeKind getKind(Symbol<SymKind::Type> type);
326 TypeKind getKind(const StringData& type);
328 bool isTypeAbstract(Symbol<SymKind::Type> type);
329 bool isTypeAbstract(const StringData& type);
331 bool isTypeFinal(Symbol<SymKind::Type> type);
332 bool isTypeFinal(const StringData& type);
334 bool isAttrIndexed(const StringData& attr) const;
335 std::string debugIndexedAttrs() const;
338 * Return a hash representing the given path's last-known checksum.
340 Optional<SHA1> getSha1Hash(Path path) const;
343 * For each file, update the SymbolMap with the given file facts.
345 * On success set m_clock to the given clock, and schedule a thread
346 * to update the DB with the new information.
348 * If the `since` token is nonempty and does not correspond to
349 * either our in-memory clock or the clock in the DB, throw an
350 * UpdateExc and do not perform any writes. The caller should
351 * initiate a fresh Watchman query with a `since` token
352 * corresponding to the clock in the map.
354 * since: An opaque token originating from Watchman representing the
355 * beginning of this update's time interval. `since` should either
356 * be empty (if we're initializing the map from scratch) or should
357 * be the timestamp currently in either the map or the DB (if we're
358 * incrementally updating the map).
360 * clock: An opaque token originating from Watchman representing the
361 * end of this update's time interval. After updating, this clock
362 * will be stored in the SymbolMap and DB.
364 * alteredPaths: A list of all paths which have changed between the
365 * last two queries to Watchman (represented by `since` and
366 * `clock`). This vector must have the same number of elements as
367 * `facts`.
369 * deletedPaths: A list of all paths which have been deleted between
370 * the last two queries to Watchman (represented by `since` and
371 * `clock`).
373 * alteredPathFacts: A list of all symbols found in the
374 * `alteredPaths`. Must be the same size as `alteredPaths`, and
375 * elements must be in the same order.
377 void update(
378 const Clock& since,
379 const Clock& clock,
380 std::vector<std::filesystem::path> alteredPaths,
381 std::vector<std::filesystem::path> deletedPaths,
382 std::vector<FileFacts> alteredPathFacts); // throws(SQLiteExc)
385 * Return an opaque token representing how up to date this map is.
387 * This token originated from Watchman.
389 Clock getClock() const noexcept;
392 * Throws an exception if facts sqlite database is corrupted/invalid.
394 * Currently only checks the sql DB for validation, not anything in map.
396 void validate(const std::set<std::string>& types_to_ignore);
399 * Return an opaque token representing how up to date the SQLite DB is.
401 * This token originated from Watchman.
403 Clock dbClock() const; // throws(SQLiteExc)
406 * Return the one and only path where `symbol` is defined.
408 * If `symbol` is not defined, return nullptr.
410 * If `symbol` is defined in more than one path, return either one of the
411 * paths that defines `symbol`, or return `nullptr`. In Hack, it's a bug to
412 * define the same symbol in multiple paths, so we leave this behavior
413 * flexible.
415 * Query the DB if there is no data about `symbol` in the given
416 * symbolMap, and add any information found to the corresponding symbolMap if
417 * so.
419 template <SymKind k>
420 Path getSymbolPath(Symbol<k> symbol); // throws(SQLiteExc)
423 * Return all the symbols of the kind corresponding to symbolMap
424 * defined in the given path.
426 * Query the DB if there is no data about `path` in the given
427 * symbolMap, and add any information found to the corresponding symbolMap if
428 * so.
430 template <SymKind k>
431 typename PathToSymbolsMap<k>::PathSymbolMap::Values getPathSymbols(Path path);
433 void waitForDBUpdate();
434 void waitForDBUpdate(std::chrono::milliseconds timeoutMs);
437 * Return a map from path to hash for every path we know about.
439 hphp_hash_map<Path, SHA1> getAllPathsWithHashes() const;
441 std::unique_ptr<folly::Executor> m_exec;
443 struct Data {
444 Data();
447 * A Watchman clock representing how up-to-date this map is.
449 * If its `m_clock` string is empty, then this map has not yet updated.
451 Clock m_clock;
454 * Version numbers which get bumped each time a path changes. We filter out
455 * the facts in our data structures which have version numbers older than
456 * the ones in this map.
458 std::shared_ptr<PathVersions> m_versions;
461 * Maps between symbols and the paths defining them.
463 PathToSymbolsMap<SymKind::Type> m_typePath;
464 PathToSymbolsMap<SymKind::Function> m_functionPath;
465 PathToSymbolsMap<SymKind::Constant> m_constantPath;
466 PathToSymbolsMap<SymKind::Module> m_modulePath;
467 PathToSymbolsMap<SymKind::ModuleMembership> m_moduleMembershipPath;
470 * Future chain and queue holding the work that needs to be done before the
471 * DB is considered up to date.
473 std::queue<UpdateDBWorkItem> m_updateDBWork;
474 folly::FutureSplitter<folly::Unit> m_updateDBFuture{folly::makeFuture()};
476 struct TypeInfo {
477 using KindAndFlags = std::pair<TypeKind, int>;
479 void setKindAndFlags(
480 Symbol<SymKind::Type> type,
481 Path path,
482 TypeKind kind,
483 int flags) {
484 auto& defs = m_map[type];
485 for (auto& [existingPath, existingInfo] : defs) {
486 if (existingPath == path) {
487 existingInfo = {kind, flags};
488 return;
491 defs.push_back({path, {kind, flags}});
494 Optional<std::pair<TypeKind, int>> getKindAndFlags(
495 Symbol<SymKind::Type> type,
496 Path path) const {
497 auto const it = m_map.find(type);
498 if (it == m_map.end()) {
499 return std::nullopt;
501 for (auto& [existingPath, info] : it->second) {
502 if (existingPath == path) {
503 return info;
506 return std::nullopt;
509 // {type: (path, (kind, flags))}
510 hphp_hash_map<
511 Symbol<SymKind::Type>,
512 std::vector<std::pair<Path, KindAndFlags>>>
513 m_map;
514 } m_typeKind;
517 * True if the file exists, false if the file is deleted.
519 hphp_hash_map<Path, bool> m_fileExistsMap;
522 * Maps between types and their subtypes/supertypes.
524 InheritanceInfo m_inheritanceInfo;
527 * Maps between types and the attributes that decorate them.
529 AttributeMap<TypeDecl> m_typeAttrs;
532 * Maps between type aliases and the attributes that decorate them.
534 AttributeMap<TypeDecl> m_typeAliasAttrs;
537 * Maps between methods and the attributes that decorate them.
539 AttributeMap<MethodDecl> m_methodAttrs;
542 * Maps between files and the attributes that decorate them.
544 AttributeMap<Path> m_fileAttrs;
547 * 40-byte hex strings representing the last-known SHA1 checksums of
548 * each file we've seen
550 hphp_hash_map<Path, SHA1> m_sha1Hashes;
553 * Parse the given path and store all its data in the map.
555 void updatePath(
556 Path path,
557 FileFacts facts,
558 const hphp_vector_set<Symbol<SymKind::Type>>& indexedMethodAttrs);
561 * Remove the given path from the map, along with all data associated with
562 * the path.
564 void removePath(Path path);
567 private:
568 void waitForDBUpdateImpl(HPHP::Optional<std::chrono::milliseconds> timeoutMs);
570 * Update the DB on the time interval beginning at `since` and
571 * ending at `clock`.
573 * We throw an UpdateExc if the `since` token does not match the clock
574 * in the DB, and we don't catch SQLiteExc from the underlying SQLite layer.
576 void updateDB(
577 const Clock& since,
578 const Clock& clock,
579 const std::vector<std::filesystem::path>& alteredPaths,
580 const std::vector<std::filesystem::path>& deletedPaths,
581 const std::vector<FileFacts>& alteredPathFacts) const; // throws
584 * Replace all facts in the DB with in-memory facts about the given path.
586 void updateDBPath(
587 AutoloadDB& db,
588 const std::filesystem::path& path,
589 const FileFacts& facts) const;
592 * Mark `derivedType` as inheriting from each of the `baseTypes`.
594 void setBaseTypes(
595 Path path,
596 Symbol<SymKind::Type> derivedType,
597 rust::Vec<rust::String> baseTypes);
600 * Load information from the DB about who the given `derivedType` inherits.
602 void loadBaseTypesFromDB(
603 AutoloadDB& db,
604 Path path,
605 Symbol<SymKind::Type> derivedType);
608 * Helper function to read from and write to m_synchronizedData.
610 * readFn: ((const Data&) -> Optional<Ret>)
611 * GetFromDBFn: ((AutoloadDB&) -> DataFromDB)
612 * writeFn: ((Data&, DataFromDB) -> Ret)
614 template <
615 typename Ret,
616 typename ReadFn,
617 typename GetFromDBFn,
618 typename WriteFn>
619 Ret readOrUpdate(ReadFn readFn, GetFromDBFn getFromDBFn, WriteFn writeFn);
622 * Return a thread-local connection to the DB associated with this map.
624 std::shared_ptr<AutoloadDB> getDB() const;
627 * Return the type's kind (class/interface/enum) along with an
628 * abstract/final bitmask.
630 std::pair<TypeKind, TypeFlagMask> getKindAndFlags(Symbol<SymKind::Type> type);
631 std::pair<TypeKind, TypeFlagMask> getKindAndFlags(
632 Symbol<SymKind::Type> type,
633 Path path);
635 std::atomic<bool> m_useDB = false;
636 // Used to prioritize updates over caching. Pending updates increment this
637 // count, while caching only occurs if this count is at 0.
638 std::atomic<size_t> m_updatesInFlight = 0;
640 folly::Synchronized<Data, folly::SharedMutexWritePriority> m_syncedData;
642 const std::filesystem::path m_root;
643 const std::string m_schemaHash;
644 AutoloadDBVault m_dbVault;
645 const hphp_vector_set<Symbol<SymKind::Type>> m_indexedMethodAttrs;
646 const bool m_enableBlockingDbWait;
647 const std::chrono::milliseconds m_blockingDbWaitTimeout;
648 const bool m_useSymbolMapForGetFilesWithAttrAndAnyVal;
651 } // namespace Facts
652 } // namespace HPHP