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