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