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