BlinkGCPluginConsumer.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "BlinkGCPluginConsumer.h"
  5. #include <algorithm>
  6. #include <set>
  7. #include "BadPatternFinder.h"
  8. #include "CheckDispatchVisitor.h"
  9. #include "CheckFieldsVisitor.h"
  10. #include "CheckFinalizerVisitor.h"
  11. #include "CheckGCRootsVisitor.h"
  12. #include "CheckTraceVisitor.h"
  13. #include "CollectVisitor.h"
  14. #include "JsonWriter.h"
  15. #include "RecordInfo.h"
  16. #include "clang/AST/RecursiveASTVisitor.h"
  17. #include "clang/Sema/Sema.h"
  18. using namespace clang;
  19. namespace {
  20. // Use a local RAV implementation to simply collect all FunctionDecls marked for
  21. // late template parsing. This happens with the flag -fdelayed-template-parsing,
  22. // which is on by default in MSVC-compatible mode.
  23. std::set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) {
  24. struct Visitor : public RecursiveASTVisitor<Visitor> {
  25. bool VisitFunctionDecl(FunctionDecl* function_decl) {
  26. if (function_decl->isLateTemplateParsed())
  27. late_parsed_decls.insert(function_decl);
  28. return true;
  29. }
  30. std::set<FunctionDecl*> late_parsed_decls;
  31. } v;
  32. v.TraverseDecl(decl);
  33. return v.late_parsed_decls;
  34. }
  35. class EmptyStmtVisitor : public RecursiveASTVisitor<EmptyStmtVisitor> {
  36. public:
  37. static bool isEmpty(Stmt* stmt) {
  38. EmptyStmtVisitor visitor;
  39. visitor.TraverseStmt(stmt);
  40. return visitor.empty_;
  41. }
  42. bool WalkUpFromCompoundStmt(CompoundStmt* stmt) {
  43. empty_ = stmt->body_empty();
  44. return false;
  45. }
  46. bool VisitStmt(Stmt*) {
  47. empty_ = false;
  48. return false;
  49. }
  50. private:
  51. EmptyStmtVisitor() : empty_(true) {}
  52. bool empty_;
  53. };
  54. const CXXRecordDecl* GetFirstTemplateArgAsCXXRecordDecl(
  55. const CXXRecordDecl* gc_base) {
  56. if (const auto* gc_base_template_id =
  57. dyn_cast<ClassTemplateSpecializationDecl>(gc_base)) {
  58. const TemplateArgumentList& gc_args =
  59. gc_base_template_id->getTemplateArgs();
  60. if (!gc_args.size() || gc_args[0].getKind() != TemplateArgument::Type)
  61. return nullptr;
  62. return gc_args[0].getAsType()->getAsCXXRecordDecl();
  63. }
  64. return nullptr;
  65. }
  66. } // namespace
  67. BlinkGCPluginConsumer::BlinkGCPluginConsumer(
  68. clang::CompilerInstance& instance,
  69. const BlinkGCPluginOptions& options)
  70. : instance_(instance),
  71. reporter_(instance),
  72. options_(options),
  73. cache_(instance),
  74. json_(0) {
  75. // Only check structures in the blink and WebKit namespaces.
  76. options_.checked_namespaces.insert("blink");
  77. options_.checked_namespaces.insert("cppgc");
  78. // Ignore GC implementation files.
  79. options_.ignored_directories.push_back(
  80. "third_party/blink/renderer/platform/heap/");
  81. options_.ignored_directories.push_back("v8/src/heap/cppgc/");
  82. options_.ignored_directories.push_back("v8/src/heap/cppgc-js/");
  83. options_.allowed_directories.push_back(
  84. "third_party/blink/renderer/platform/heap/test/");
  85. }
  86. void BlinkGCPluginConsumer::HandleTranslationUnit(ASTContext& context) {
  87. // Don't run the plugin if the compilation unit is already invalid.
  88. if (reporter_.hasErrorOccurred())
  89. return;
  90. ParseFunctionTemplates(context.getTranslationUnitDecl());
  91. CollectVisitor visitor;
  92. visitor.TraverseDecl(context.getTranslationUnitDecl());
  93. if (options_.dump_graph) {
  94. std::error_code err;
  95. SmallString<128> OutputFile(instance_.getFrontendOpts().OutputFile);
  96. llvm::sys::path::replace_extension(OutputFile, "graph.json");
  97. json_ = JsonWriter::from(instance_.createOutputFile(
  98. OutputFile, // OutputPath
  99. true, // Binary
  100. true, // RemoveFileOnSignal
  101. false, // UseTemporary
  102. false)); // CreateMissingDirectories
  103. if (!err && json_) {
  104. json_->OpenList();
  105. } else {
  106. json_ = 0;
  107. llvm::errs()
  108. << "[blink-gc] "
  109. << "Failed to create an output file for the object graph.\n";
  110. }
  111. }
  112. for (const auto& record : visitor.record_decls())
  113. CheckRecord(cache_.Lookup(record));
  114. for (const auto& method : visitor.trace_decls())
  115. CheckTracingMethod(method);
  116. if (json_) {
  117. json_->CloseList();
  118. delete json_;
  119. json_ = 0;
  120. }
  121. FindBadPatterns(context, reporter_, options_);
  122. }
  123. void BlinkGCPluginConsumer::ParseFunctionTemplates(TranslationUnitDecl* decl) {
  124. if (!instance_.getLangOpts().DelayedTemplateParsing)
  125. return; // Nothing to do.
  126. std::set<FunctionDecl*> late_parsed_decls = GetLateParsedFunctionDecls(decl);
  127. clang::Sema& sema = instance_.getSema();
  128. for (const FunctionDecl* fd : late_parsed_decls) {
  129. assert(fd->isLateTemplateParsed());
  130. if (!Config::IsTraceMethod(fd))
  131. continue;
  132. if (instance_.getSourceManager().isInSystemHeader(
  133. instance_.getSourceManager().getSpellingLoc(fd->getLocation())))
  134. continue;
  135. // Force parsing and AST building of the yet-uninstantiated function
  136. // template trace method bodies.
  137. clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd].get();
  138. sema.LateTemplateParser(sema.OpaqueParser, *lpt);
  139. }
  140. }
  141. void BlinkGCPluginConsumer::CheckRecord(RecordInfo* info) {
  142. if (IsIgnored(info))
  143. return;
  144. CXXRecordDecl* record = info->record();
  145. // TODO: what should we do to check unions?
  146. if (record->isUnion())
  147. return;
  148. // If this is the primary template declaration, check its specializations.
  149. if (record->isThisDeclarationADefinition() &&
  150. record->getDescribedClassTemplate()) {
  151. ClassTemplateDecl* tmpl = record->getDescribedClassTemplate();
  152. for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
  153. it != tmpl->spec_end();
  154. ++it) {
  155. CheckClass(cache_.Lookup(*it));
  156. }
  157. return;
  158. }
  159. CheckClass(info);
  160. }
  161. void BlinkGCPluginConsumer::CheckClass(RecordInfo* info) {
  162. if (!info)
  163. return;
  164. if (CXXMethodDecl* trace = info->GetTraceMethod()) {
  165. if (info->IsStackAllocated())
  166. reporter_.TraceMethodForStackAllocatedClass(info, trace);
  167. if (trace->isPure())
  168. reporter_.ClassDeclaresPureVirtualTrace(info, trace);
  169. } else if (info->RequiresTraceMethod()) {
  170. reporter_.ClassRequiresTraceMethod(info);
  171. }
  172. // Check polymorphic classes that are GC-derived or have a trace method.
  173. if (info->record()->hasDefinition() && info->record()->isPolymorphic()) {
  174. // TODO: Check classes that inherit a trace method.
  175. CXXMethodDecl* trace = info->GetTraceMethod();
  176. if (trace || info->IsGCDerived())
  177. CheckPolymorphicClass(info, trace);
  178. }
  179. {
  180. CheckFieldsVisitor visitor(options_);
  181. if (visitor.ContainsInvalidFields(info))
  182. reporter_.ClassContainsInvalidFields(info, visitor.invalid_fields());
  183. }
  184. if (info->IsGCDerived()) {
  185. // Check that CRTP pattern for GCed classes is correctly used.
  186. if (auto* base_spec = info->GetDirectGCBase()) {
  187. // Skip the check if base_spec name is dependent. The check will occur
  188. // later for actual specializations.
  189. if (!base_spec->getType()->isDependentType()) {
  190. const CXXRecordDecl* base_decl =
  191. base_spec->getType()->getAsCXXRecordDecl();
  192. const CXXRecordDecl* first_arg =
  193. GetFirstTemplateArgAsCXXRecordDecl(base_decl);
  194. // The last check is for redeclaratation cases, for example, when
  195. // explicit instantiation declaration is followed by the corresponding
  196. // explicit instantiation definition.
  197. if (!first_arg ||
  198. first_arg->getFirstDecl() != info->record()->getFirstDecl()) {
  199. reporter_.ClassMustCRTPItself(info, base_decl, base_spec);
  200. }
  201. }
  202. }
  203. // It is illegal for a class to be both stack allocated and garbage
  204. // collected.
  205. if (info->IsStackAllocated()) {
  206. for (auto& base : info->GetBases()) {
  207. RecordInfo* base_info = base.second.info();
  208. if (Config::IsGCBase(base_info->name()) || base_info->IsGCDerived()) {
  209. reporter_.StackAllocatedDerivesGarbageCollected(info, &base.second);
  210. }
  211. }
  212. }
  213. if (!info->IsGCMixin()) {
  214. CheckLeftMostDerived(info);
  215. CheckDispatch(info);
  216. if (CXXMethodDecl* newop = info->DeclaresNewOperator())
  217. if (!Config::IsIgnoreAnnotated(newop))
  218. reporter_.ClassOverridesNew(info, newop);
  219. }
  220. {
  221. CheckGCRootsVisitor visitor(options_);
  222. if (visitor.ContainsGCRoots(info))
  223. reporter_.ClassContainsGCRoots(info, visitor.gc_roots());
  224. }
  225. if (info->NeedsFinalization())
  226. CheckFinalization(info);
  227. }
  228. DumpClass(info);
  229. }
  230. CXXRecordDecl* BlinkGCPluginConsumer::GetDependentTemplatedDecl(
  231. const Type& type) {
  232. const TemplateSpecializationType* tmpl_type =
  233. type.getAs<TemplateSpecializationType>();
  234. if (!tmpl_type)
  235. return 0;
  236. TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl();
  237. if (!tmpl_decl)
  238. return 0;
  239. return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl());
  240. }
  241. // The GC infrastructure assumes that if the vtable of a polymorphic
  242. // base-class is not initialized for a given object (ie, it is partially
  243. // initialized) then the object does not need to be traced. Thus, we must
  244. // ensure that any polymorphic class with a trace method does not have any
  245. // tractable fields that are initialized before we are sure that the vtable
  246. // and the trace method are both defined. There are two cases that need to
  247. // hold to satisfy that assumption:
  248. //
  249. // 1. If trace is virtual, then it must be defined in the left-most base.
  250. // This ensures that if the vtable is initialized then it contains a pointer
  251. // to the trace method.
  252. //
  253. // 2. If trace is non-virtual, then the trace method is defined and we must
  254. // ensure that the left-most base defines a vtable. This ensures that the
  255. // first thing to be initialized when constructing the object is the vtable
  256. // itself.
  257. void BlinkGCPluginConsumer::CheckPolymorphicClass(
  258. RecordInfo* info,
  259. CXXMethodDecl* trace) {
  260. CXXRecordDecl* left_most = info->record();
  261. CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
  262. CXXRecordDecl* left_most_base = 0;
  263. while (it != left_most->bases_end()) {
  264. left_most_base = it->getType()->getAsCXXRecordDecl();
  265. if (!left_most_base && it->getType()->isDependentType())
  266. left_most_base = RecordInfo::GetDependentTemplatedDecl(*it->getType());
  267. // TODO: Find a way to correctly check actual instantiations
  268. // for dependent types. The escape below will be hit, eg, when
  269. // we have a primary template with no definition and
  270. // specializations for each case (such as SupplementBase) in
  271. // which case we don't succeed in checking the required
  272. // properties.
  273. if (!left_most_base || !left_most_base->hasDefinition())
  274. return;
  275. StringRef name = left_most_base->getName();
  276. // We know GCMixin base defines virtual trace.
  277. if (Config::IsGCMixinBase(name))
  278. return;
  279. // Stop with the left-most prior to a safe polymorphic base (a safe base
  280. // is non-polymorphic and contains no fields).
  281. if (Config::IsSafePolymorphicBase(name))
  282. break;
  283. left_most = left_most_base;
  284. it = left_most->bases_begin();
  285. }
  286. if (RecordInfo* left_most_info = cache_.Lookup(left_most)) {
  287. // Check condition (1):
  288. if (trace && trace->isVirtual()) {
  289. if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) {
  290. if (trace->isVirtual())
  291. return;
  292. }
  293. reporter_.BaseClassMustDeclareVirtualTrace(info, left_most);
  294. return;
  295. }
  296. // Check condition (2):
  297. if (DeclaresVirtualMethods(left_most))
  298. return;
  299. if (left_most_base) {
  300. // Get the base next to the "safe polymorphic base"
  301. if (it != left_most->bases_end())
  302. ++it;
  303. if (it != left_most->bases_end()) {
  304. if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) {
  305. if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) {
  306. if (DeclaresVirtualMethods(next_left_most))
  307. return;
  308. reporter_.LeftMostBaseMustBePolymorphic(info, next_left_most);
  309. return;
  310. }
  311. }
  312. }
  313. }
  314. reporter_.LeftMostBaseMustBePolymorphic(info, left_most);
  315. }
  316. }
  317. CXXRecordDecl* BlinkGCPluginConsumer::GetLeftMostBase(
  318. CXXRecordDecl* left_most) {
  319. CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
  320. while (it != left_most->bases_end()) {
  321. if (it->getType()->isDependentType())
  322. left_most = RecordInfo::GetDependentTemplatedDecl(*it->getType());
  323. else
  324. left_most = it->getType()->getAsCXXRecordDecl();
  325. if (!left_most || !left_most->hasDefinition())
  326. return 0;
  327. it = left_most->bases_begin();
  328. }
  329. return left_most;
  330. }
  331. bool BlinkGCPluginConsumer::DeclaresVirtualMethods(CXXRecordDecl* decl) {
  332. CXXRecordDecl::method_iterator it = decl->method_begin();
  333. for (; it != decl->method_end(); ++it)
  334. if (it->isVirtual() && !it->isPure())
  335. return true;
  336. return false;
  337. }
  338. void BlinkGCPluginConsumer::CheckLeftMostDerived(RecordInfo* info) {
  339. CXXRecordDecl* left_most = GetLeftMostBase(info->record());
  340. if (!left_most)
  341. return;
  342. if (!Config::IsGCBase(left_most->getName()) || Config::IsGCMixinBase(left_most->getName()))
  343. reporter_.ClassMustLeftMostlyDeriveGC(info);
  344. }
  345. void BlinkGCPluginConsumer::CheckDispatch(RecordInfo* info) {
  346. CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod();
  347. CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod();
  348. if (!trace_dispatch && !finalize_dispatch)
  349. return;
  350. CXXRecordDecl* base = trace_dispatch ? trace_dispatch->getParent()
  351. : finalize_dispatch->getParent();
  352. // Check that dispatch methods are defined at the base.
  353. if (base == info->record()) {
  354. if (!trace_dispatch)
  355. reporter_.MissingTraceDispatchMethod(info);
  356. }
  357. // Check that classes implementing manual dispatch do not have vtables.
  358. if (info->record()->isPolymorphic()) {
  359. reporter_.VirtualAndManualDispatch(
  360. info, trace_dispatch ? trace_dispatch : finalize_dispatch);
  361. }
  362. // If this is a non-abstract class check that it is dispatched to.
  363. // TODO: Create a global variant of this local check. We can only check if
  364. // the dispatch body is known in this compilation unit.
  365. if (info->IsConsideredAbstract())
  366. return;
  367. const FunctionDecl* defn;
  368. if (trace_dispatch && trace_dispatch->isDefined(defn)) {
  369. CheckDispatchVisitor visitor(info);
  370. visitor.TraverseStmt(defn->getBody());
  371. if (!visitor.dispatched_to_receiver())
  372. reporter_.MissingTraceDispatch(defn, info);
  373. }
  374. if (finalize_dispatch && finalize_dispatch->isDefined(defn)) {
  375. CheckDispatchVisitor visitor(info);
  376. visitor.TraverseStmt(defn->getBody());
  377. if (!visitor.dispatched_to_receiver())
  378. reporter_.MissingFinalizeDispatch(defn, info);
  379. }
  380. }
  381. // TODO: Should we collect destructors similar to trace methods?
  382. void BlinkGCPluginConsumer::CheckFinalization(RecordInfo* info) {
  383. CXXDestructorDecl* dtor = info->record()->getDestructor();
  384. if (!dtor || !dtor->hasBody())
  385. return;
  386. CheckFinalizerVisitor visitor(&cache_);
  387. visitor.TraverseCXXMethodDecl(dtor);
  388. if (!visitor.finalized_fields().empty()) {
  389. reporter_.FinalizerAccessesFinalizedFields(dtor,
  390. visitor.finalized_fields());
  391. }
  392. }
  393. void BlinkGCPluginConsumer::CheckTracingMethod(CXXMethodDecl* method) {
  394. RecordInfo* parent = cache_.Lookup(method->getParent());
  395. if (IsIgnored(parent))
  396. return;
  397. // Check templated tracing methods by checking the template instantiations.
  398. // Specialized templates are handled as ordinary classes.
  399. if (ClassTemplateDecl* tmpl =
  400. parent->record()->getDescribedClassTemplate()) {
  401. for (ClassTemplateDecl::spec_iterator it = tmpl->spec_begin();
  402. it != tmpl->spec_end();
  403. ++it) {
  404. // Check trace using each template instantiation as the holder.
  405. if (Config::IsTemplateInstantiation(*it))
  406. CheckTraceOrDispatchMethod(cache_.Lookup(*it), method);
  407. }
  408. return;
  409. }
  410. CheckTraceOrDispatchMethod(parent, method);
  411. }
  412. void BlinkGCPluginConsumer::CheckTraceOrDispatchMethod(
  413. RecordInfo* parent,
  414. CXXMethodDecl* method) {
  415. Config::TraceMethodType trace_type = Config::GetTraceMethodType(method);
  416. if (trace_type == Config::TRACE_AFTER_DISPATCH_METHOD ||
  417. !parent->GetTraceDispatchMethod()) {
  418. CheckTraceMethod(parent, method, trace_type);
  419. }
  420. // Dispatch methods are checked when we identify subclasses.
  421. }
  422. void BlinkGCPluginConsumer::CheckTraceMethod(
  423. RecordInfo* parent,
  424. CXXMethodDecl* trace,
  425. Config::TraceMethodType trace_type) {
  426. // A trace method must not override any non-virtual trace methods.
  427. if (trace_type == Config::TRACE_METHOD) {
  428. for (auto& base : parent->GetBases())
  429. if (CXXMethodDecl* other = base.second.info()->InheritsNonVirtualTrace())
  430. reporter_.OverriddenNonVirtualTrace(parent, trace, other);
  431. }
  432. CheckTraceVisitor visitor(trace, parent, &cache_);
  433. visitor.TraverseCXXMethodDecl(trace);
  434. for (auto& base : parent->GetBases())
  435. if (!base.second.IsProperlyTraced())
  436. reporter_.BaseRequiresTracing(parent, trace, base.first);
  437. for (auto& field : parent->GetFields()) {
  438. if (!field.second.IsProperlyTraced() ||
  439. field.second.IsInproperlyTraced()) {
  440. // Report one or more tracing-related field errors.
  441. reporter_.FieldsImproperlyTraced(parent, trace);
  442. break;
  443. }
  444. }
  445. }
  446. void BlinkGCPluginConsumer::DumpClass(RecordInfo* info) {
  447. if (!json_)
  448. return;
  449. json_->OpenObject();
  450. json_->Write("name", info->record()->getQualifiedNameAsString());
  451. json_->Write("loc", GetLocString(info->record()->getBeginLoc()));
  452. json_->CloseObject();
  453. class DumpEdgeVisitor : public RecursiveEdgeVisitor {
  454. public:
  455. DumpEdgeVisitor(JsonWriter* json) : json_(json) {}
  456. void DumpEdge(RecordInfo* src,
  457. RecordInfo* dst,
  458. const std::string& lbl,
  459. const Edge::LivenessKind& kind,
  460. const std::string& loc) {
  461. json_->OpenObject();
  462. json_->Write("src", src->record()->getQualifiedNameAsString());
  463. json_->Write("dst", dst->record()->getQualifiedNameAsString());
  464. json_->Write("lbl", lbl);
  465. json_->Write("kind", kind);
  466. json_->Write("loc", loc);
  467. json_->Write("ptr",
  468. !Parent() ? "val" :
  469. Parent()->IsRawPtr() ?
  470. (static_cast<RawPtr*>(Parent())->HasReferenceType() ?
  471. "reference" : "raw") :
  472. Parent()->IsRefPtr() ? "ref" :
  473. Parent()->IsUniquePtr() ? "unique" :
  474. (Parent()->IsMember() || Parent()->IsWeakMember()) ? "mem" :
  475. "val");
  476. json_->CloseObject();
  477. }
  478. void DumpField(RecordInfo* src, FieldPoint* point, const std::string& loc) {
  479. src_ = src;
  480. point_ = point;
  481. loc_ = loc;
  482. point_->edge()->Accept(this);
  483. }
  484. void AtValue(Value* e) override {
  485. // The liveness kind of a path from the point to this value
  486. // is given by the innermost place that is non-strong.
  487. Edge::LivenessKind kind = Edge::kStrong;
  488. for (Context::iterator it = context().begin(); it != context().end();
  489. ++it) {
  490. Edge::LivenessKind pointer_kind = (*it)->Kind();
  491. if (pointer_kind != Edge::kStrong) {
  492. kind = pointer_kind;
  493. break;
  494. }
  495. }
  496. DumpEdge(
  497. src_, e->value(), point_->field()->getNameAsString(), kind, loc_);
  498. }
  499. private:
  500. JsonWriter* json_;
  501. RecordInfo* src_;
  502. FieldPoint* point_;
  503. std::string loc_;
  504. };
  505. DumpEdgeVisitor visitor(json_);
  506. for (auto& base : info->GetBases())
  507. visitor.DumpEdge(info, base.second.info(), "<super>", Edge::kStrong,
  508. GetLocString(base.second.spec().getBeginLoc()));
  509. for (auto& field : info->GetFields())
  510. visitor.DumpField(info, &field.second,
  511. GetLocString(field.second.field()->getBeginLoc()));
  512. }
  513. std::string BlinkGCPluginConsumer::GetLocString(SourceLocation loc) {
  514. const SourceManager& source_manager = instance_.getSourceManager();
  515. PresumedLoc ploc = source_manager.getPresumedLoc(loc);
  516. if (ploc.isInvalid())
  517. return "";
  518. std::string loc_str;
  519. llvm::raw_string_ostream os(loc_str);
  520. os << ploc.getFilename()
  521. << ":" << ploc.getLine()
  522. << ":" << ploc.getColumn();
  523. return os.str();
  524. }
  525. bool BlinkGCPluginConsumer::IsIgnored(RecordInfo* record) {
  526. return (!record ||
  527. !InCheckedNamespace(record) ||
  528. IsIgnoredClass(record) ||
  529. InIgnoredDirectory(record));
  530. }
  531. bool BlinkGCPluginConsumer::IsIgnoredClass(RecordInfo* info) {
  532. // Ignore any class prefixed by SameSizeAs. These are used in
  533. // Blink to verify class sizes and don't need checking.
  534. const std::string SameSizeAs = "SameSizeAs";
  535. if (info->name().compare(0, SameSizeAs.size(), SameSizeAs) == 0)
  536. return true;
  537. return (options_.ignored_classes.find(info->name()) !=
  538. options_.ignored_classes.end());
  539. }
  540. bool BlinkGCPluginConsumer::InIgnoredDirectory(RecordInfo* info) {
  541. std::string filename;
  542. if (!GetFilename(info->record()->getBeginLoc(), &filename))
  543. return false; // TODO: should we ignore non-existing file locations?
  544. #if defined(_WIN32)
  545. std::replace(filename.begin(), filename.end(), '\\', '/');
  546. #endif
  547. for (const auto& ignored_dir : options_.ignored_directories)
  548. if (filename.find(ignored_dir) != std::string::npos) {
  549. for (const auto& allowed_dir : options_.allowed_directories) {
  550. if (filename.find(allowed_dir) != std::string::npos)
  551. return false;
  552. }
  553. return true;
  554. }
  555. return false;
  556. }
  557. bool BlinkGCPluginConsumer::InCheckedNamespace(RecordInfo* info) {
  558. if (!info)
  559. return false;
  560. for (DeclContext* context = info->record()->getDeclContext();
  561. !context->isTranslationUnit();
  562. context = context->getParent()) {
  563. if (NamespaceDecl* decl = dyn_cast<NamespaceDecl>(context)) {
  564. if (decl->isAnonymousNamespace())
  565. return true;
  566. if (options_.checked_namespaces.find(decl->getNameAsString()) !=
  567. options_.checked_namespaces.end()) {
  568. return true;
  569. }
  570. }
  571. }
  572. return false;
  573. }
  574. bool BlinkGCPluginConsumer::GetFilename(SourceLocation loc,
  575. std::string* filename) {
  576. const SourceManager& source_manager = instance_.getSourceManager();
  577. SourceLocation spelling_location = source_manager.getSpellingLoc(loc);
  578. PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location);
  579. if (ploc.isInvalid()) {
  580. // If we're in an invalid location, we're looking at things that aren't
  581. // actually stated in the source.
  582. return false;
  583. }
  584. *filename = ploc.getFilename();
  585. return true;
  586. }