PageRenderTime 59ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/compiler/package.cpp

https://gitlab.com/0072016/0072016-PHP.LLC
C++ | 363 lines | 303 code | 42 blank | 18 comment | 71 complexity | 97f718e6a7832ff0d3abe0d2681ca5ba MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file 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. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/compiler/package.h"
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <unistd.h>
  20. #include <dirent.h>
  21. #include <fstream>
  22. #include <map>
  23. #include <memory>
  24. #include <set>
  25. #include <utility>
  26. #include <vector>
  27. #include <folly/String.h>
  28. #include "hphp/compiler/analysis/analysis_result.h"
  29. #include "hphp/compiler/parser/parser.h"
  30. #include "hphp/compiler/analysis/symbol_table.h"
  31. #include "hphp/compiler/analysis/variable_table.h"
  32. #include "hphp/compiler/option.h"
  33. #include "hphp/compiler/json.h"
  34. #include "hphp/util/process.h"
  35. #include "hphp/util/logger.h"
  36. #include "hphp/util/exception.h"
  37. #include "hphp/util/job-queue.h"
  38. #include "hphp/runtime/base/file-util.h"
  39. #include "hphp/runtime/base/execution-context.h"
  40. #include "hphp/runtime/base/program-functions.h"
  41. using namespace HPHP;
  42. using std::set;
  43. ///////////////////////////////////////////////////////////////////////////////
  44. Package::Package(const char *root, bool bShortTags /* = true */,
  45. bool bAspTags /* = false */)
  46. : m_files(4000), m_dispatcher(0), m_lineCount(0), m_charCount(0) {
  47. m_root = FileUtil::normalizeDir(root);
  48. m_ar = AnalysisResultPtr(new AnalysisResult());
  49. m_fileCache = std::make_shared<FileCache>();
  50. }
  51. void Package::addAllFiles(bool force) {
  52. if (Option::PackageDirectories.empty() && Option::PackageFiles.empty()) {
  53. addDirectory("/", force);
  54. } else {
  55. for (auto iter = Option::PackageDirectories.begin();
  56. iter != Option::PackageDirectories.end(); ++iter) {
  57. addDirectory(*iter, force);
  58. }
  59. for (auto iter = Option::PackageFiles.begin();
  60. iter != Option::PackageFiles.end(); ++iter) {
  61. addSourceFile((*iter).c_str());
  62. }
  63. }
  64. }
  65. void Package::addInputList(const char *listFileName) {
  66. assert(listFileName && *listFileName);
  67. FILE *f = fopen(listFileName, "r");
  68. if (f == nullptr) {
  69. throw Exception("Unable to open %s: %s", listFileName,
  70. folly::errnoStr(errno).c_str());
  71. }
  72. char fileName[PATH_MAX];
  73. while (fgets(fileName, sizeof(fileName), f)) {
  74. int len = strlen(fileName);
  75. if (fileName[len - 1] == '\n') fileName[len - 1] = '\0';
  76. len = strlen(fileName);
  77. if (len) {
  78. if (fileName[len - 1] == '/') {
  79. addDirectory(fileName, false);
  80. } else {
  81. addSourceFile(fileName);
  82. }
  83. }
  84. }
  85. fclose(f);
  86. }
  87. void Package::addStaticFile(const char *fileName) {
  88. assert(fileName && *fileName);
  89. m_extraStaticFiles.insert(fileName);
  90. }
  91. void Package::addStaticDirectory(const std::string path) {
  92. m_staticDirectories.insert(path);
  93. }
  94. void Package::addDirectory(const std::string &path, bool force) {
  95. addDirectory(path.c_str(), force);
  96. }
  97. void Package::addDirectory(const char *path, bool force) {
  98. m_directories.insert(path);
  99. addPHPDirectory(path, force);
  100. }
  101. void Package::addPHPDirectory(const char *path, bool force) {
  102. std::vector<std::string> files;
  103. if (force) {
  104. FileUtil::find(files, m_root, path, true);
  105. } else {
  106. FileUtil::find(files, m_root, path, true,
  107. &Option::PackageExcludeDirs, &Option::PackageExcludeFiles);
  108. Option::FilterFiles(files, Option::PackageExcludePatterns);
  109. }
  110. auto const rootSize = m_root.size();
  111. for (auto const& file : files) {
  112. assert(file.substr(0, rootSize) == m_root);
  113. m_filesToParse.insert(file.substr(rootSize));
  114. }
  115. }
  116. void Package::getFiles(std::vector<std::string> &files) const {
  117. assert(m_filesToParse.empty());
  118. files.clear();
  119. files.reserve(m_files.size());
  120. for (unsigned int i = 0; i < m_files.size(); i++) {
  121. const char *fileName = m_files.at(i);
  122. files.push_back(fileName);
  123. }
  124. }
  125. std::shared_ptr<FileCache> Package::getFileCache() {
  126. for (auto iter = m_directories.begin();
  127. iter != m_directories.end(); ++iter) {
  128. std::vector<std::string> files;
  129. FileUtil::find(files, m_root, iter->c_str(), false,
  130. &Option::PackageExcludeStaticDirs,
  131. &Option::PackageExcludeStaticFiles);
  132. Option::FilterFiles(files, Option::PackageExcludeStaticPatterns);
  133. for (auto& file : files) {
  134. auto const rpath = file.substr(m_root.size());
  135. if (!m_fileCache->fileExists(rpath.c_str())) {
  136. Logger::Verbose("saving %s", file.c_str());
  137. m_fileCache->write(rpath.c_str(), file.c_str());
  138. }
  139. }
  140. }
  141. for (auto iter = m_staticDirectories.begin();
  142. iter != m_staticDirectories.end(); ++iter) {
  143. std::vector<std::string> files;
  144. FileUtil::find(files, m_root, iter->c_str(), false);
  145. for (auto& file : files) {
  146. auto const rpath = file.substr(m_root.size());
  147. if (!m_fileCache->fileExists(rpath.c_str())) {
  148. Logger::Verbose("saving %s", file.c_str());
  149. m_fileCache->write(rpath.c_str(), file.c_str());
  150. }
  151. }
  152. }
  153. for (auto iter = m_extraStaticFiles.begin();
  154. iter != m_extraStaticFiles.end(); ++iter) {
  155. const char *file = iter->c_str();
  156. if (!m_fileCache->fileExists(file)) {
  157. auto const fullpath = m_root + file;
  158. Logger::Verbose("saving %s", fullpath.c_str());
  159. m_fileCache->write(file, fullpath.c_str());
  160. }
  161. }
  162. for (auto iter = m_discoveredStaticFiles.begin();
  163. iter != m_discoveredStaticFiles.end(); ++iter) {
  164. const char *file = iter->first.c_str();
  165. if (!m_fileCache->fileExists(file)) {
  166. const char *fullpath = iter->second.c_str();
  167. Logger::Verbose("saving %s", fullpath[0] ? fullpath : file);
  168. if (fullpath[0]) {
  169. m_fileCache->write(file, fullpath);
  170. } else {
  171. m_fileCache->write(file);
  172. }
  173. }
  174. }
  175. return m_fileCache;
  176. }
  177. ///////////////////////////////////////////////////////////////////////////////
  178. class ParserWorker :
  179. public JobQueueWorker<std::pair<const char *,bool>, Package*, true, true> {
  180. public:
  181. bool m_ret;
  182. ParserWorker() : m_ret(true) {}
  183. void doJob(JobType job) override {
  184. bool ret;
  185. try {
  186. Package *package = m_context;
  187. ret = package->parseImpl(job.first);
  188. } catch (Exception &e) {
  189. Logger::Error("%s", e.getMessage().c_str());
  190. ret = false;
  191. } catch (...) {
  192. Logger::Error("Fatal: An unexpected exception was thrown");
  193. m_ret = false;
  194. return;
  195. }
  196. if (!ret && job.second) {
  197. Logger::Error("Fatal: Unable to stat/parse %s", job.first);
  198. m_ret = false;
  199. }
  200. }
  201. void onThreadExit() override {
  202. hphp_memory_cleanup();
  203. }
  204. };
  205. void Package::addSourceFile(const char *fileName, bool check /* = false */) {
  206. if (fileName && *fileName) {
  207. Lock lock(m_mutex);
  208. auto canonFileName =
  209. FileUtil::canonicalize(String(fileName)).toCppString();
  210. bool inserted = m_filesToParse.insert(canonFileName).second;
  211. if (inserted && m_dispatcher) {
  212. ((JobQueueDispatcher<ParserWorker>*)m_dispatcher)->enqueue(
  213. std::make_pair(m_files.add(fileName), check));
  214. }
  215. }
  216. }
  217. bool Package::parse(bool check) {
  218. if (m_filesToParse.empty()) {
  219. return true;
  220. }
  221. unsigned int threadCount = Option::ParserThreadCount;
  222. if (threadCount > m_filesToParse.size()) {
  223. threadCount = m_filesToParse.size();
  224. }
  225. if (threadCount <= 0) threadCount = 1;
  226. JobQueueDispatcher<ParserWorker>
  227. dispatcher(threadCount, true, 0, false, this);
  228. m_dispatcher = &dispatcher;
  229. std::set<std::string> files;
  230. files.swap(m_filesToParse);
  231. dispatcher.start();
  232. for (auto iter = files.begin(), end = files.end(); iter != end; ++iter) {
  233. addSourceFile((*iter).c_str(), check);
  234. }
  235. dispatcher.waitEmpty();
  236. m_dispatcher = 0;
  237. std::vector<ParserWorker*> workers;
  238. dispatcher.getWorkers(workers);
  239. for (unsigned int i = 0; i < workers.size(); i++) {
  240. ParserWorker *worker = workers[i];
  241. if (!worker->m_ret) return false;
  242. }
  243. return true;
  244. }
  245. bool Package::parse(const char *fileName) {
  246. return parseImpl(m_files.add(fileName));
  247. }
  248. bool Package::parseImpl(const char *fileName) {
  249. assert(fileName);
  250. if (fileName[0] == 0) return false;
  251. std::string fullPath;
  252. if (fileName[0] == '/') {
  253. fullPath = fileName;
  254. } else {
  255. fullPath = m_root + fileName;
  256. }
  257. struct stat sb;
  258. if (stat(fullPath.c_str(), &sb)) {
  259. if (fullPath.find(' ') == std::string::npos) {
  260. Logger::Error("Unable to stat file %s", fullPath.c_str());
  261. }
  262. return false;
  263. }
  264. if ((sb.st_mode & S_IFMT) == S_IFDIR) {
  265. Logger::Error("Unable to parse directory: %s", fullPath.c_str());
  266. return false;
  267. }
  268. int lines = 0;
  269. try {
  270. Logger::Verbose("parsing %s ...", fullPath.c_str());
  271. Scanner scanner(fullPath, Option::GetScannerType(), true);
  272. Compiler::Parser parser(scanner, fileName, m_ar, sb.st_size);
  273. parser.parse();
  274. lines = parser.line1();
  275. } catch (FileOpenException &e) {
  276. Logger::Error("%s", e.getMessage().c_str());
  277. return false;
  278. }
  279. m_lineCount += lines;
  280. struct stat fst;
  281. stat(fullPath.c_str(), &fst);
  282. m_charCount += fst.st_size;
  283. Lock lock(m_mutex);
  284. if (m_extraStaticFiles.find(fileName) == m_extraStaticFiles.end() &&
  285. m_discoveredStaticFiles.find(fileName) == m_discoveredStaticFiles.end()) {
  286. if (Option::CachePHPFile) {
  287. m_discoveredStaticFiles[fileName] = fullPath;
  288. } else {
  289. m_discoveredStaticFiles[fileName] = "";
  290. }
  291. }
  292. return true;
  293. }
  294. ///////////////////////////////////////////////////////////////////////////////
  295. void Package::saveStatsToFile(const char *filename, int totalSeconds) const {
  296. std::ofstream f(filename);
  297. if (f) {
  298. JSON::CodeError::OutputStream o(f, m_ar);
  299. JSON::CodeError::MapStream ms(o);
  300. ms.add("FileCount", getFileCount())
  301. .add("LineCount", getLineCount())
  302. .add("CharCount", getCharCount())
  303. .add("FunctionCount", m_ar->getFunctionCount())
  304. .add("ClassCount", m_ar->getClassCount())
  305. .add("TotalTime", totalSeconds);
  306. if (getLineCount()) {
  307. ms.add("AvgCharPerLine", getCharCount() / getLineCount());
  308. }
  309. if (m_ar->getFunctionCount()) {
  310. ms.add("AvgLinePerFunc", getLineCount()/m_ar->getFunctionCount());
  311. }
  312. ms.add("VariableTableFunctions");
  313. JSON::CodeError::ListStream ls(o);
  314. for (auto const& f : m_ar->m_variableTableFunctions) {
  315. ls << f;
  316. }
  317. ls.done();
  318. ms.done();
  319. f.close();
  320. }
  321. }