Home | History | Annotate | Download | only in blink_gc_plugin
      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.
     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:
     44   static bool isEmpty(Stmt* stmt) {
     45     EmptyStmtVisitor visitor;
     46     visitor.TraverseStmt(stmt);
     47     return visitor.empty_;
     48   }
     49 
     50   bool WalkUpFromCompoundStmt(CompoundStmt* stmt) {
     51     empty_ = stmt->body_empty();
     52     return false;
     53   }
     54   bool VisitStmt(Stmt*) {
     55     empty_ = false;
     56     return false;
     57   }
     58  private:
     59   EmptyStmtVisitor() : empty_(true) {}
     60   bool empty_;
     61 };
     62 
     63 }  // namespace
     64 
     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 
     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 
    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 
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    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?
    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 
    453 void BlinkGCPluginConsumer::CheckUnneededFinalization(RecordInfo* info) {
    454   if (!HasNonEmptyFinalizer(info))
    455     reporter_.ClassDoesNotRequireFinalization(info);
    456 }
    457 
    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 
    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 
    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 
    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 
    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 
    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 
    668 bool BlinkGCPluginConsumer::IsIgnored(RecordInfo* record) {
    669   return (!record ||
    670           !InCheckedNamespace(record) ||
    671           IsIgnoredClass(record) ||
    672           InIgnoredDirectory(record));
    673 }
    674 
    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 
    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 
    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 
    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