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