Add group and permissions settings to the Facts DB
[hiphop-php.git] / hphp / runtime / ext / facts / ext_facts.cpp
blob5f469ac6bccd76401bfa5d07e8dc45450b28ba8d
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 #include <chrono>
18 #include <functional>
19 #include <iomanip>
20 #include <mutex>
21 #include <optional>
22 #include <pwd.h>
23 #include <sstream>
24 #include <string>
25 #include <string_view>
26 #include <sys/types.h>
28 #include <folly/Hash.h>
29 #include <folly/concurrency/ConcurrentHashMap.h>
30 #include <folly/executors/GlobalExecutor.h>
31 #include <folly/experimental/io/FsUtil.h>
32 #include <folly/io/async/EventBaseThread.h>
33 #include <folly/json.h>
35 #include <watchman/cppclient/WatchmanClient.h>
37 #include "hphp/runtime/base/autoload-handler.h"
38 #include "hphp/runtime/base/autoload-map.h"
39 #include "hphp/runtime/base/builtin-functions.h"
40 #include "hphp/runtime/base/config.h"
41 #include "hphp/runtime/base/init-fini-node.h"
42 #include "hphp/runtime/base/static-string-table.h"
43 #include "hphp/runtime/base/string-data.h"
44 #include "hphp/runtime/base/type-string.h"
45 #include "hphp/runtime/base/variable-serializer.h"
46 #include "hphp/runtime/ext/extension.h"
47 #include "hphp/runtime/ext/facts/autoload-db.h"
48 #include "hphp/runtime/ext/facts/ext_facts.h"
49 #include "hphp/runtime/ext/facts/string-ptr.h"
50 #include "hphp/runtime/ext/facts/watchman-autoload-map.h"
51 #include "hphp/runtime/ext/facts/watchman.h"
52 #include "hphp/runtime/vm/treadmill.h"
53 #include "hphp/util/build-info.h"
54 #include "hphp/util/hash-map.h"
55 #include "hphp/util/hash.h"
56 #include "hphp/util/user-info.h"
57 #include "hphp/zend/zend-string.h"
59 TRACE_SET_MOD(facts);
61 namespace HPHP {
62 namespace Facts {
63 namespace {
65 constexpr std::string_view kEUIDPlaceholder = "%{euid}";
66 constexpr std::string_view kSchemaPlaceholder = "%{schema}";
67 constexpr std::chrono::seconds kDefaultExpirationTime{30 * 60};
68 constexpr size_t kDBVersion = 3;
70 struct RepoOptionsParseExc : public std::runtime_error {
71 explicit RepoOptionsParseExc(std::string msg)
72 : std::runtime_error{std::move(msg)} {
76 /**
77 * Get the directory containing the given RepoOptions file. We define this to
78 * be the root of the repository we're autoloading.
80 folly::fs::path getRepoRoot(const RepoOptions& options) {
81 return folly::fs::canonical(folly::fs::path{options.path()}.parent_path());
84 folly::fs::path
85 getDBPath(const folly::fs::path& root, const folly::dynamic& queryExpr) {
86 std::stringstream schemaHash;
87 schemaHash << std::hex
88 << folly::hash::hash_combine(
89 kDBVersion,
90 queryExpr.hash(),
91 std::hash<std::string_view>{}(root.native()));
93 std::string pathTemplate{RuntimeOption::AutoloadDBPath};
96 size_t idx = pathTemplate.find(kEUIDPlaceholder);
97 if (idx != std::string::npos) {
98 pathTemplate.replace(
99 idx, kEUIDPlaceholder.size(), folly::to<std::string>(geteuid()));
104 size_t idx = pathTemplate.find(kSchemaPlaceholder);
105 if (idx != std::string::npos) {
106 pathTemplate.replace(
107 idx, kSchemaPlaceholder.size(), std::move(schemaHash).str());
111 folly::fs::path dbPath = pathTemplate;
112 if (dbPath.is_relative()) {
113 dbPath = root / dbPath;
116 return folly::fs::system_complete(dbPath);
119 ::gid_t getGroup() {
120 // Resolve the group to a unix gid
121 if (RuntimeOption::AutoloadDBGroup.empty()) {
122 return -1;
124 try {
125 GroupInfo grp{RuntimeOption::AutoloadDBGroup.c_str()};
126 return grp.gr->gr_gid;
127 } catch (const Exception& e) {
128 Logger::Warning(folly::sformat(
129 "Can't resolve {} to a gid: {}",
130 RuntimeOption::AutoloadDBGroup,
131 e.what()));
132 return -1;
136 ::mode_t getDBPerms() {
137 try {
138 ::mode_t res = std::stoi(RuntimeOption::AutoloadDBPerms, 0, 8);
139 FTRACE(3, "Converted {} to {}\n", RuntimeOption::AutoloadDBPerms, res);
140 return res;
141 } catch (const std::exception& e) {
142 Logger::Warning(folly::sformat(
143 "Error running std::stoi on \"Autoload.DB.Perms\": {}", e.what()));
144 return 0644;
148 DBData getDBData(
149 const folly::fs::path& root,
150 const folly::dynamic& queryExpr,
151 const RepoOptions& repoOptions) {
152 assertx(root.is_absolute());
154 auto trustedDBPath = [&]() -> folly::fs::path {
155 folly::fs::path trusted{repoOptions.trustedDBPath()};
156 if (trusted.empty()) {
157 return trusted;
159 // If the trustedDBPath is relative, make sure we resolve it relative
160 // to the repo root rather than the current working directory
161 if (trusted.is_relative()) {
162 trusted = root / trusted;
164 try {
165 return folly::fs::canonical(trusted);
166 } catch (const folly::fs::filesystem_error& e) {
167 throw RepoOptionsParseExc{folly::sformat(
168 "Error resolving Autoload.TrustedDBPath = {}: {}",
169 trusted.native().c_str(),
170 e.what())};
172 }();
174 if (trustedDBPath.empty()) {
175 ::gid_t gid = getGroup();
176 return DBData::readWrite(getDBPath(root, queryExpr), gid, getDBPerms());
177 } else {
178 return DBData::readOnly(std::move(trustedDBPath));
182 // Convenience wrapper for std::string_view
183 inline strhash_t hash_string_view_cs(std::string_view s) {
184 return hash_string_cs(s.data(), s.size());
188 * List of options making a WatchmanAutoloadMap unique
190 struct WatchmanAutoloadMapKey {
192 static WatchmanAutoloadMapKey get(const RepoOptions& repoOptions) {
193 auto root = getRepoRoot(repoOptions);
195 auto queryExpr = [&]() -> folly::dynamic {
196 auto queryStr = repoOptions.autoloadQuery();
197 if (queryStr.empty()) {
198 return {};
200 try {
201 return folly::parseJson(queryStr);
202 } catch (const folly::json::parse_error& e) {
203 throw RepoOptionsParseExc{folly::sformat(
204 "Error JSON-parsing Autoload.Query = \"{}\": {}",
205 queryStr.c_str(),
206 e.what())};
208 }();
210 auto dbData = getDBData(root, queryExpr, repoOptions);
212 return WatchmanAutoloadMapKey{
213 .m_root = std::move(root),
214 .m_queryExpr = std::move(queryExpr),
215 .m_dbData = std::move(dbData)};
218 bool operator==(const WatchmanAutoloadMapKey& rhs) const noexcept {
219 return m_root == rhs.m_root && m_queryExpr == rhs.m_queryExpr &&
220 m_dbData == rhs.m_dbData;
223 std::string toString() const {
224 return folly::sformat(
225 "WatchmanAutoloadMapKey({}, {}, {})",
226 m_root.native(),
227 folly::toJson(m_queryExpr),
228 m_dbData.toString());
231 strhash_t hash() const noexcept {
232 return folly::hash::hash_combine(
233 hash_string_view_cs(m_root.native()),
234 m_queryExpr.hash(),
235 m_dbData.hash());
239 * A repo is autoloadable if we can either:
241 * 1. Use Watchman to track the files and create our own database
242 * 2. Read an existing database file somewhere
244 bool isAutoloadableRepo() const {
245 return m_queryExpr.isObject() ||
246 m_dbData.m_rwMode == SQLite::OpenMode::ReadOnly;
249 folly::fs::path m_root;
250 folly::dynamic m_queryExpr;
251 DBData m_dbData;
254 } // namespace
255 } // namespace Facts
256 } // namespace HPHP
258 namespace std {
259 template <> struct hash<HPHP::Facts::WatchmanAutoloadMapKey> {
260 size_t operator()(const HPHP::Facts::WatchmanAutoloadMapKey& k) const {
261 return static_cast<size_t>(k.hash());
264 } // namespace std
266 namespace HPHP {
267 namespace Facts {
268 namespace {
271 * Sent to AutoloadHandler so AutoloadHandler can create
272 * WatchmanAutoloadMaps across the open-source / FB-only boundary.
274 struct WatchmanAutoloadMapFactory final : public FactsFactory {
276 WatchmanAutoloadMapFactory() = default;
277 WatchmanAutoloadMapFactory(const WatchmanAutoloadMapFactory&) = delete;
278 WatchmanAutoloadMapFactory(WatchmanAutoloadMapFactory&&) = delete;
279 WatchmanAutoloadMapFactory&
280 operator=(const WatchmanAutoloadMapFactory&) = delete;
281 WatchmanAutoloadMapFactory& operator=(WatchmanAutoloadMapFactory&&) = delete;
282 ~WatchmanAutoloadMapFactory() override = default;
284 FactsStore* getForOptions(const RepoOptions& options) override;
287 * Delete AutoloadMaps which haven't been accessed in the last
288 * `expirationTime` seconds.
290 void garbageCollectUnusedAutoloadMaps(std::chrono::seconds expirationTime);
292 private:
293 std::mutex m_mutex;
296 * Map from root to AutoloadMap
298 hphp_hash_map<WatchmanAutoloadMapKey, std::shared_ptr<FactsStore>> m_maps;
301 * Map from root to time we last accessed the AutoloadMap
303 hphp_hash_map<
304 WatchmanAutoloadMapKey,
305 std::chrono::time_point<std::chrono::steady_clock>>
306 m_lastUsed;
309 struct Facts final : Extension {
310 Facts() : Extension("facts", "1.0") {
313 void moduleLoad(const IniSetting::Map& ini, Hdf config) override {
315 // Create all resources at a deterministic time to avoid SIOF
316 m_data = FactsData{};
318 // An AutoloadMap may be freed after this many seconds since its last use
319 m_data->m_expirationTime = std::chrono::seconds{Config::GetInt64(
320 ini,
321 config,
322 "Autoload.MapIdleGCTimeSeconds",
323 kDefaultExpirationTime.count())};
324 if (m_data->m_expirationTime != kDefaultExpirationTime) {
325 FTRACE(
327 "Autoload.MapIdleGCTimeSeconds = {}\n",
328 m_data->m_expirationTime.count());
331 auto watchmanSocket =
332 Config::GetString(ini, config, "watchman.socket.default");
333 if (!watchmanSocket.empty()) {
334 FTRACE(3, "watchman.socket.default = {}\n", watchmanSocket);
335 m_data->m_watchmanDefaultSocket = std::move(watchmanSocket);
338 auto watchmanRootSocket =
339 Config::GetString(ini, config, "watchman.socket.root");
340 if (!watchmanRootSocket.empty()) {
341 FTRACE(3, "watchman.socket.root = {}\n", watchmanRootSocket);
342 m_data->m_watchmanRootSocket = std::move(watchmanRootSocket);
345 auto excluded = Config::GetStrVector(ini, config, "Autoload.ExcludedRepos");
346 for (auto const& repo : excluded) {
347 try {
348 m_data->m_excludedRepos.insert(folly::fs::canonical(repo).native());
349 } catch (const folly::fs::filesystem_error& e) {
350 Logger::Info(
351 "Could not disable native autoloader for %s: %s\n",
352 repo.c_str(),
353 e.what());
358 void moduleInit() override {
359 HHVM_NAMED_FE(HH\\Facts\\enabled, HHVM_FN(facts_enabled));
360 HHVM_NAMED_FE(HH\\Facts\\db_path, HHVM_FN(facts_db_path));
361 HHVM_NAMED_FE(HH\\Facts\\type_to_path, HHVM_FN(facts_type_to_path));
362 HHVM_NAMED_FE(HH\\Facts\\function_to_path, HHVM_FN(facts_function_to_path));
363 HHVM_NAMED_FE(HH\\Facts\\constant_to_path, HHVM_FN(facts_constant_to_path));
364 HHVM_NAMED_FE(
365 HH\\Facts\\type_alias_to_path, HHVM_FN(facts_type_alias_to_path));
367 HHVM_NAMED_FE(HH\\Facts\\path_to_types, HHVM_FN(facts_path_to_types));
368 HHVM_NAMED_FE(
369 HH\\Facts\\path_to_functions, HHVM_FN(facts_path_to_functions));
370 HHVM_NAMED_FE(
371 HH\\Facts\\path_to_constants, HHVM_FN(facts_path_to_constants));
372 HHVM_NAMED_FE(
373 HH\\Facts\\path_to_type_aliases, HHVM_FN(facts_path_to_type_aliases));
374 HHVM_NAMED_FE(HH\\Facts\\type_name, HHVM_FN(facts_type_name));
375 HHVM_NAMED_FE(HH\\Facts\\kind, HHVM_FN(facts_kind));
376 HHVM_NAMED_FE(HH\\Facts\\is_abstract, HHVM_FN(facts_is_abstract));
377 HHVM_NAMED_FE(HH\\Facts\\is_final, HHVM_FN(facts_is_final));
378 HHVM_NAMED_FE(HH\\Facts\\subtypes, HHVM_FN(facts_subtypes));
379 HHVM_NAMED_FE(
380 HH\\Facts\\transitive_subtypes, HHVM_FN(facts_transitive_subtypes));
381 HHVM_NAMED_FE(HH\\Facts\\supertypes, HHVM_FN(facts_supertypes));
382 HHVM_NAMED_FE(
383 HH\\Facts\\types_with_attribute, HHVM_FN(facts_types_with_attribute));
384 HHVM_NAMED_FE(
385 HH\\Facts\\type_aliases_with_attribute,
386 HHVM_FN(facts_type_aliases_with_attribute));
387 HHVM_NAMED_FE(HH\\Facts\\type_attributes, HHVM_FN(facts_type_attributes));
388 HHVM_NAMED_FE(
389 HH\\Facts\\type_alias_attributes, HHVM_FN(facts_type_alias_attributes));
390 HHVM_NAMED_FE(
391 HH\\Facts\\type_attribute_parameters,
392 HHVM_FN(facts_type_attribute_parameters));
393 HHVM_NAMED_FE(
394 HH\\Facts\\type_alias_attribute_parameters,
395 HHVM_FN(facts_type_alias_attribute_parameters));
396 HHVM_NAMED_FE(HH\\Facts\\all_types, HHVM_FN(facts_all_types));
397 HHVM_NAMED_FE(HH\\Facts\\all_functions, HHVM_FN(facts_all_functions));
398 HHVM_NAMED_FE(HH\\Facts\\all_constants, HHVM_FN(facts_all_constants));
399 HHVM_NAMED_FE(HH\\Facts\\all_type_aliases, HHVM_FN(facts_all_type_aliases));
400 loadSystemlib();
402 if (!RuntimeOption::AutoloadEnabled) {
403 FTRACE(
405 "Autoload.Enabled is not true, not enabling native "
406 "autoloader.\n");
407 return;
410 if (RuntimeOption::AutoloadDBPath.empty()) {
411 FTRACE(1, "Autoload.DB.Path was empty, not enabling native autoloader.\n");
412 return;
415 if (!m_data->m_watchmanDefaultSocket) {
416 FTRACE(2, "watchman.socket.default was not provided.\n");
419 if (!m_data->m_watchmanRootSocket) {
420 FTRACE(2, "watchman.socket.root was not provided.\n");
423 m_data->m_mapFactory = std::make_unique<WatchmanAutoloadMapFactory>();
424 FactsFactory::setInstance(m_data->m_mapFactory.get());
427 void moduleShutdown() override {
428 // Destroy all resources at a deterministic time to avoid SDOF
429 FactsFactory::setInstance(nullptr);
430 m_data = {};
433 std::chrono::seconds getExpirationTime() const {
434 return m_data->m_expirationTime;
437 const std::optional<std::string>& getWatchmanDefaultSocket() const {
438 return m_data->m_watchmanDefaultSocket;
441 const std::optional<std::string>& getWatchmanRootSocket() const {
442 return m_data->m_watchmanRootSocket;
445 Watchman& getWatchmanClient(const folly::fs::path& root) {
446 auto it = m_data->m_watchmanClients.find(root.native());
447 if (it != m_data->m_watchmanClients.end()) {
448 return *it->second;
450 auto watchmanSocket = getPerUserWatchmanSocket();
451 if (!watchmanSocket) {
452 FTRACE(3, "No per-user watchman socket found.\n");
453 watchmanSocket = getWatchmanDefaultSocket();
456 return *m_data->m_watchmanClients
457 .insert(root.native(), Watchman::get(root, watchmanSocket))
458 .first->second;
461 private:
462 // Add new members to this struct instead of the top level so we can be sure
463 // your new member is destroyed at the right time.
464 struct FactsData {
465 std::chrono::seconds m_expirationTime{30 * 60};
466 std::optional<std::string> m_watchmanDefaultSocket;
467 std::optional<std::string> m_watchmanRootSocket;
468 hphp_hash_set<std::string> m_excludedRepos;
469 folly::ConcurrentHashMap<std::string, std::shared_ptr<Watchman>>
470 m_watchmanClients{};
471 std::unique_ptr<WatchmanAutoloadMapFactory> m_mapFactory;
473 std::optional<FactsData> m_data;
476 * Discover who owns the given repo and return the Watchman socket
477 * corresponding to that user.
479 std::optional<std::string> getPerUserWatchmanSocket() {
480 IniSetting::Map ini = IniSetting::Map::object;
481 Hdf config;
482 auto* repoOptions = g_context->getRepoOptionsForRequest();
483 if (!repoOptions) {
484 return {};
487 // Figure out who owns the repo we're trying to run code in
488 auto repoRoot = getRepoRoot(*repoOptions);
489 int repoRootFD = ::open(repoRoot.native().c_str(), O_DIRECTORY | O_RDONLY);
490 if (repoRootFD == -1) {
491 return {};
493 SCOPE_EXIT {
494 ::close(repoRootFD);
496 struct ::stat hstat {};
497 if (::fstat(repoRootFD, &hstat) != 0) {
498 return {};
501 // The repo is owned by root, so use a special root socket
502 if (hstat.st_uid == 0) {
503 auto const& rootSock = getWatchmanRootSocket();
504 FTRACE(
506 "{} is owned by root, looking for socket at {}\n",
507 repoRoot.native(),
508 rootSock ? *rootSock : "<none>");
509 return rootSock;
512 // Find the `watchman.socket` setting in the repo owner's
513 // SandboxConfFile (usually a .hphp file somewhere in their home
514 // directory).
515 UserInfo info{hstat.st_uid};
516 auto user = std::string{info.pw->pw_name};
517 auto homePath = RuntimeOption::GetHomePath(user);
518 if (!homePath) {
519 return {};
521 auto confFileName = (*homePath) / RuntimeOption::SandboxConfFile;
522 bool success =
523 RuntimeOption::ReadPerUserSettings(confFileName, ini, config);
524 if (!success) {
525 return {};
527 auto sock = Config::GetString(ini, config, "watchman.socket.default");
528 if (sock.empty()) {
529 return {};
531 return {std::move(sock)};
534 } s_ext;
536 FactsStore*
537 WatchmanAutoloadMapFactory::getForOptions(const RepoOptions& options) {
539 auto mapKey = [&]() -> std::optional<WatchmanAutoloadMapKey> {
540 try {
541 auto mk = WatchmanAutoloadMapKey::get(options);
542 if (!mk.isAutoloadableRepo()) {
543 return std::nullopt;
545 return {std::move(mk)};
546 } catch (const RepoOptionsParseExc& e) {
547 Logger::Warning("%s\n", e.what());
548 return std::nullopt;
550 }();
552 if (!mapKey) {
553 return nullptr;
556 std::unique_lock g{m_mutex};
558 // Mark the fact that we've accessed the map
559 m_lastUsed.insert_or_assign(*mapKey, std::chrono::steady_clock::now());
561 // Try to return a corresponding WatchmanAutoloadMap
562 auto const it = m_maps.find(*mapKey);
563 if (it != m_maps.end()) {
564 return it->second.get();
567 // We're creating a new map. This is a good sign that an existing map may
568 // be defunct, so schedule a cleanup job to check.
569 Treadmill::enqueue(
570 [this] { garbageCollectUnusedAutoloadMaps(s_ext.getExpirationTime()); });
572 if (mapKey->m_dbData.m_rwMode == SQLite::OpenMode::ReadOnly) {
573 FTRACE(
575 "Loading {} from trusted Autoload DB at {}\n",
576 mapKey->m_root.native(),
577 mapKey->m_dbData.m_path.native());
578 return m_maps
579 .insert(
580 {*mapKey,
581 std::make_shared<WatchmanAutoloadMap>(
582 mapKey->m_root, mapKey->m_dbData)})
583 .first->second.get();
586 assertx(mapKey->m_queryExpr.isObject());
587 auto map = std::make_shared<WatchmanAutoloadMap>(
588 mapKey->m_root,
589 mapKey->m_dbData,
590 mapKey->m_queryExpr,
591 s_ext.getWatchmanClient(mapKey->m_root));
593 if (RuntimeOption::ServerExecutionMode()) {
594 map->subscribe();
597 return m_maps.insert({*mapKey, std::move(map)}).first->second.get();
600 void WatchmanAutoloadMapFactory::garbageCollectUnusedAutoloadMaps(
601 std::chrono::seconds expirationTime) {
602 auto mapsToRemove = [&]() -> std::vector<std::shared_ptr<FactsStore>> {
603 std::unique_lock g{m_mutex};
605 // If a map was last used before this time, remove it
606 auto deadline = std::chrono::steady_clock::now() - expirationTime;
608 std::vector<WatchmanAutoloadMapKey> keysToRemove;
609 for (auto const& [mapKey, _] : m_maps) {
610 auto lastUsedIt = m_lastUsed.find(mapKey);
611 if (lastUsedIt == m_lastUsed.end() || lastUsedIt->second < deadline) {
612 keysToRemove.push_back(mapKey);
616 std::vector<std::shared_ptr<FactsStore>> maps;
617 maps.reserve(keysToRemove.size());
618 for (auto const& mapKey : keysToRemove) {
619 FTRACE(2, "Evicting WatchmanAutoloadMap: {}\n", mapKey.toString());
620 auto it = m_maps.find(mapKey);
621 if (it != m_maps.end()) {
622 maps.push_back(std::move(it->second));
623 m_maps.erase(it);
625 m_lastUsed.erase(mapKey);
627 return maps;
628 }();
630 // Final references to shared_ptr<Facts> fall out of scope
631 // while `m_mutex` lock is not held
634 FactsStore& getFactsOrThrow() {
635 auto* facts = AutoloadHandler::s_instance->getFacts();
636 if (facts == nullptr) {
637 SystemLib::throwInvalidOperationExceptionObject(
638 "Native Facts is not enabled. Call HH\\Facts\\enabled() to determine "
639 "if native Facts is enabled for the current request.");
641 return *facts;
644 } // namespace
645 } // namespace Facts
647 bool HHVM_FUNCTION(facts_enabled) {
648 return AutoloadHandler::s_instance->getFacts() != nullptr;
651 Variant HHVM_FUNCTION(facts_db_path, const String& rootStr) {
652 // Turn rootStr into an absolute path.
653 auto root = [&]() -> std::optional<folly::fs::path> {
654 folly::fs::path maybeRoot{rootStr.get()->slice()};
655 if (maybeRoot.is_absolute()) {
656 return maybeRoot;
658 // The given root is a relative path, so find the directory where the
659 // current request's `.hhvmconfig.hdf` file lives and resolve relative to
660 // that.
661 auto requestOptions = g_context->getRepoOptionsForRequest();
662 if (!requestOptions) {
663 return std::nullopt;
665 return folly::fs::path{requestOptions->path()}.parent_path() / maybeRoot;
666 }();
667 if (!root) {
668 FTRACE(2, "Error resolving {}\n", rootStr.slice());
669 return Variant{Variant::NullInit{}};
671 assertx(root->is_absolute());
673 auto optionPath = *root / ".hhvmconfig.hdf";
674 FTRACE(3, "Got options at {}\n", optionPath.native());
675 auto const& repoOptions = RepoOptions::forFile(optionPath.native().c_str());
677 try {
678 return Variant{Facts::WatchmanAutoloadMapKey::get(repoOptions)
679 .m_dbData.m_path.native()};
680 } catch (const Facts::RepoOptionsParseExc& e) {
681 throw_invalid_operation_exception(makeStaticString(e.what()));
685 Variant HHVM_FUNCTION(facts_type_to_path, const String& typeName) {
686 auto path = Facts::getFactsOrThrow().getTypeFile(typeName);
687 if (!path) {
688 return Variant{Variant::NullInit{}};
689 } else {
690 return Variant{*path};
694 Variant HHVM_FUNCTION(facts_function_to_path, const String& functionName) {
695 auto path = Facts::getFactsOrThrow().getFunctionFile(functionName);
696 if (!path) {
697 return Variant{Variant::NullInit{}};
698 } else {
699 return Variant{*path};
703 Variant HHVM_FUNCTION(facts_constant_to_path, const String& constantName) {
704 auto path = Facts::getFactsOrThrow().getConstantFile(constantName);
705 if (!path) {
706 return Variant{Variant::NullInit{}};
707 } else {
708 return Variant{*path};
712 Variant HHVM_FUNCTION(facts_type_alias_to_path, const String& typeAliasName) {
713 auto path = Facts::getFactsOrThrow().getTypeAliasFile(typeAliasName);
714 if (!path) {
715 return Variant{Variant::NullInit{}};
716 } else {
717 return Variant{*path};
721 Array HHVM_FUNCTION(facts_path_to_types, const String& path) {
722 return Facts::getFactsOrThrow().getFileTypes(path);
725 Array HHVM_FUNCTION(facts_path_to_functions, const String& path) {
726 return Facts::getFactsOrThrow().getFileFunctions(path);
729 Array HHVM_FUNCTION(facts_path_to_constants, const String& path) {
730 return Facts::getFactsOrThrow().getFileConstants(path);
733 Array HHVM_FUNCTION(facts_path_to_type_aliases, const String& path) {
734 return Facts::getFactsOrThrow().getFileTypeAliases(path);
737 Variant HHVM_FUNCTION(facts_type_name, const String& type) {
738 return Facts::getFactsOrThrow().getTypeName(type);
741 Variant HHVM_FUNCTION(facts_kind, const String& type) {
742 return Facts::getFactsOrThrow().getKind(type);
745 bool HHVM_FUNCTION(facts_is_abstract, const String& type) {
746 return Facts::getFactsOrThrow().isTypeAbstract(type);
749 bool HHVM_FUNCTION(facts_is_final, const String& type) {
750 return Facts::getFactsOrThrow().isTypeFinal(type);
753 Array HHVM_FUNCTION(
754 facts_subtypes, const String& baseType, const Variant& filters) {
755 return Facts::getFactsOrThrow().getDerivedTypes(baseType, filters);
758 Array HHVM_FUNCTION(
759 facts_transitive_subtypes, const String& baseType, const Variant& filters) {
760 return Facts::getFactsOrThrow().getTransitiveDerivedTypes(baseType, filters);
763 Array HHVM_FUNCTION(
764 facts_supertypes, const String& derivedType, const Variant& filters) {
765 return Facts::getFactsOrThrow().getBaseTypes(derivedType, filters);
768 Array HHVM_FUNCTION(facts_types_with_attribute, const String& attr) {
769 return Facts::getFactsOrThrow().getTypesWithAttribute(attr);
772 Array HHVM_FUNCTION(facts_type_aliases_with_attribute, const String& attr) {
773 return Facts::getFactsOrThrow().getTypeAliasesWithAttribute(attr);
776 Array HHVM_FUNCTION(facts_type_attributes, const String& type) {
777 return Facts::getFactsOrThrow().getTypeAttributes(type);
780 Array HHVM_FUNCTION(facts_type_alias_attributes, const String& typeAlias) {
781 return Facts::getFactsOrThrow().getTypeAttributes(typeAlias);
784 Array HHVM_FUNCTION(
785 facts_type_attribute_parameters, const String& type, const String& attr) {
786 return Facts::getFactsOrThrow().getTypeAttrArgs(type, attr);
789 Array HHVM_FUNCTION(
790 facts_type_alias_attribute_parameters,
791 const String& type,
792 const String& attr) {
793 return Facts::getFactsOrThrow().getTypeAttrArgs(type, attr);
796 Array HHVM_FUNCTION(facts_all_types) {
797 return Facts::getFactsOrThrow().getAllTypes();
799 Array HHVM_FUNCTION(facts_all_functions) {
800 return Facts::getFactsOrThrow().getAllFunctions();
802 Array HHVM_FUNCTION(facts_all_constants) {
803 return Facts::getFactsOrThrow().getAllConstants();
805 Array HHVM_FUNCTION(facts_all_type_aliases) {
806 return Facts::getFactsOrThrow().getAllTypeAliases();
809 } // namespace HPHP