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