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