• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "Config.h"
6 #include "RecordInfo.h"
7 #include "clang/Sema/Sema.h"
8 
9 using namespace clang;
10 using std::string;
11 
RecordInfo(CXXRecordDecl * record,RecordCache * cache)12 RecordInfo::RecordInfo(CXXRecordDecl* record, RecordCache* cache)
13     : cache_(cache),
14       record_(record),
15       name_(record->getName()),
16       fields_need_tracing_(TracingStatus::Unknown()),
17       bases_(0),
18       fields_(0),
19       is_stack_allocated_(kNotComputed),
20       is_non_newable_(kNotComputed),
21       is_only_placement_newable_(kNotComputed),
22       does_need_finalization_(kNotComputed),
23       has_gc_mixin_methods_(kNotComputed),
24       is_declaring_local_trace_(kNotComputed),
25       is_eagerly_finalized_(kNotComputed),
26       determined_trace_methods_(false),
27       trace_method_(0),
28       trace_dispatch_method_(0),
29       finalize_dispatch_method_(0),
30       is_gc_derived_(false) {}
31 
~RecordInfo()32 RecordInfo::~RecordInfo() {
33   delete fields_;
34   delete bases_;
35 }
36 
37 // Get |count| number of template arguments. Returns false if there
38 // are fewer than |count| arguments or any of the arguments are not
39 // of a valid Type structure. If |count| is non-positive, all
40 // arguments are collected.
GetTemplateArgs(size_t count,TemplateArgs * output_args)41 bool RecordInfo::GetTemplateArgs(size_t count, TemplateArgs* output_args) {
42   ClassTemplateSpecializationDecl* tmpl =
43       dyn_cast<ClassTemplateSpecializationDecl>(record_);
44   if (!tmpl)
45     return false;
46   const TemplateArgumentList& args = tmpl->getTemplateArgs();
47   if (args.size() < count)
48     return false;
49   if (count <= 0)
50     count = args.size();
51   for (unsigned i = 0; i < count; ++i) {
52     TemplateArgument arg = args[i];
53     if (arg.getKind() == TemplateArgument::Type && !arg.getAsType().isNull()) {
54       output_args->push_back(arg.getAsType().getTypePtr());
55     } else {
56       return false;
57     }
58   }
59   return true;
60 }
61 
62 // Test if a record is a HeapAllocated collection.
IsHeapAllocatedCollection()63 bool RecordInfo::IsHeapAllocatedCollection() {
64   if (!Config::IsGCCollection(name_) && !Config::IsWTFCollection(name_))
65     return false;
66 
67   TemplateArgs args;
68   if (GetTemplateArgs(0, &args)) {
69     for (TemplateArgs::iterator it = args.begin(); it != args.end(); ++it) {
70       if (CXXRecordDecl* decl = (*it)->getAsCXXRecordDecl())
71         if (decl->getName() == kHeapAllocatorName)
72           return true;
73     }
74   }
75 
76   return Config::IsGCCollection(name_);
77 }
78 
HasOptionalFinalizer()79 bool RecordInfo::HasOptionalFinalizer() {
80   if (!IsHeapAllocatedCollection())
81     return false;
82   // Heap collections may have a finalizer but it is optional (i.e. may be
83   // delayed until FinalizeGarbageCollectedObject() gets called), unless there
84   // is an inline buffer. Vector, Deque, and ListHashSet can have an inline
85   // buffer.
86   if (name_ != "Vector" && name_ != "Deque" && name_ != "HeapVector" &&
87       name_ != "HeapDeque")
88     return true;
89   ClassTemplateSpecializationDecl* tmpl =
90       dyn_cast<ClassTemplateSpecializationDecl>(record_);
91   // These collections require template specialization so tmpl should always be
92   // non-null for valid code.
93   if (!tmpl)
94     return false;
95   const TemplateArgumentList& args = tmpl->getTemplateArgs();
96   if (args.size() < 2)
97     return true;
98   TemplateArgument arg = args[1];
99   // The second template argument must be void or 0 so there is no inline
100   // buffer.
101   return (arg.getKind() == TemplateArgument::Type &&
102           arg.getAsType()->isVoidType()) ||
103          (arg.getKind() == TemplateArgument::Integral &&
104           arg.getAsIntegral().getExtValue() == 0);
105 }
106 
107 // Test if a record is derived from a garbage collected base.
IsGCDerived()108 bool RecordInfo::IsGCDerived() {
109   // If already computed, return the known result.
110   if (gc_base_names_.size())
111     return is_gc_derived_;
112 
113   if (!record_->hasDefinition())
114     return false;
115 
116   // The base classes are not themselves considered garbage collected objects.
117   if (Config::IsGCBase(name_))
118     return false;
119 
120   // Walk the inheritance tree to find GC base classes.
121   walkBases();
122   return is_gc_derived_;
123 }
124 
GetDependentTemplatedDecl(const Type & type)125 CXXRecordDecl* RecordInfo::GetDependentTemplatedDecl(const Type& type) {
126   const TemplateSpecializationType* tmpl_type =
127       type.getAs<TemplateSpecializationType>();
128   if (!tmpl_type)
129     return 0;
130 
131   TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl();
132   if (!tmpl_decl)
133     return 0;
134 
135   return dyn_cast_or_null<CXXRecordDecl>(tmpl_decl->getTemplatedDecl());
136 }
137 
walkBases()138 void RecordInfo::walkBases() {
139   // This traversal is akin to CXXRecordDecl::forallBases()'s,
140   // but without stepping over dependent bases -- these might also
141   // have a "GC base name", so are to be included and considered.
142   SmallVector<const CXXRecordDecl*, 8> queue;
143 
144   const CXXRecordDecl* base_record = record();
145   while (true) {
146     for (const auto& it : base_record->bases()) {
147       const RecordType* type = it.getType()->getAs<RecordType>();
148       CXXRecordDecl* base;
149       if (!type)
150         base = GetDependentTemplatedDecl(*it.getType());
151       else {
152         base = cast_or_null<CXXRecordDecl>(type->getDecl()->getDefinition());
153         if (base)
154           queue.push_back(base);
155       }
156       if (!base)
157         continue;
158 
159       const std::string& name = base->getName();
160       if (Config::IsGCBase(name)) {
161         gc_base_names_.push_back(name);
162         is_gc_derived_ = true;
163       }
164     }
165 
166     if (queue.empty())
167       break;
168     base_record = queue.pop_back_val(); // not actually a queue.
169   }
170 }
171 
IsGCFinalized()172 bool RecordInfo::IsGCFinalized() {
173   if (!IsGCDerived())
174     return false;
175   for (const auto& gc_base : gc_base_names_) {
176     if (Config::IsGCFinalizedBase(gc_base))
177       return true;
178   }
179   return false;
180 }
181 
182 // A GC mixin is a class that inherits from a GC mixin base and has
183 // not yet been "mixed in" with another GC base class.
IsGCMixin()184 bool RecordInfo::IsGCMixin() {
185   if (!IsGCDerived() || !gc_base_names_.size())
186     return false;
187   for (const auto& gc_base : gc_base_names_) {
188       // If it is not a mixin base we are done.
189       if (!Config::IsGCMixinBase(gc_base))
190           return false;
191   }
192   // This is a mixin if all GC bases are mixins.
193   return true;
194 }
195 
196 // Test if a record is allocated on the managed heap.
IsGCAllocated()197 bool RecordInfo::IsGCAllocated() {
198   return IsGCDerived() || IsHeapAllocatedCollection();
199 }
200 
IsEagerlyFinalized()201 bool RecordInfo::IsEagerlyFinalized() {
202   if (is_eagerly_finalized_ != kNotComputed)
203     return is_eagerly_finalized_;
204 
205   is_eagerly_finalized_ = kFalse;
206   if (!IsGCFinalized())
207     return is_eagerly_finalized_;
208 
209   for (Decl* decl : record_->decls()) {
210     if (TypedefDecl* typedef_decl = dyn_cast<TypedefDecl>(decl)) {
211       if (typedef_decl->getNameAsString() != kIsEagerlyFinalizedName)
212         continue;
213       is_eagerly_finalized_ = kTrue;
214       break;
215     }
216   }
217   return is_eagerly_finalized_;
218 }
219 
HasDefinition()220 bool RecordInfo::HasDefinition() {
221   return record_->hasDefinition();
222 }
223 
Lookup(CXXRecordDecl * record)224 RecordInfo* RecordCache::Lookup(CXXRecordDecl* record) {
225   // Ignore classes annotated with the GC_PLUGIN_IGNORE macro.
226   if (!record || Config::IsIgnoreAnnotated(record))
227     return 0;
228   Cache::iterator it = cache_.find(record);
229   if (it != cache_.end())
230     return &it->second;
231   return &cache_.insert(std::make_pair(record, RecordInfo(record, this)))
232               .first->second;
233 }
234 
IsStackAllocated()235 bool RecordInfo::IsStackAllocated() {
236   if (is_stack_allocated_ == kNotComputed) {
237     is_stack_allocated_ = kFalse;
238     for (Bases::iterator it = GetBases().begin();
239          it != GetBases().end();
240          ++it) {
241       if (it->second.info()->IsStackAllocated()) {
242         is_stack_allocated_ = kTrue;
243         return is_stack_allocated_;
244       }
245     }
246     for (CXXRecordDecl::method_iterator it = record_->method_begin();
247          it != record_->method_end();
248          ++it) {
249       if (it->getNameAsString() == kNewOperatorName &&
250           it->isDeleted() &&
251           Config::IsStackAnnotated(*it)) {
252         is_stack_allocated_ = kTrue;
253         return is_stack_allocated_;
254       }
255     }
256   }
257   return is_stack_allocated_;
258 }
259 
IsNonNewable()260 bool RecordInfo::IsNonNewable() {
261   if (is_non_newable_ == kNotComputed) {
262     bool deleted = false;
263     bool all_deleted = true;
264     for (CXXRecordDecl::method_iterator it = record_->method_begin();
265          it != record_->method_end();
266          ++it) {
267       if (it->getNameAsString() == kNewOperatorName) {
268         deleted = it->isDeleted();
269         all_deleted = all_deleted && deleted;
270       }
271     }
272     is_non_newable_ = (deleted && all_deleted) ? kTrue : kFalse;
273   }
274   return is_non_newable_;
275 }
276 
IsOnlyPlacementNewable()277 bool RecordInfo::IsOnlyPlacementNewable() {
278   if (is_only_placement_newable_ == kNotComputed) {
279     bool placement = false;
280     bool new_deleted = false;
281     for (CXXRecordDecl::method_iterator it = record_->method_begin();
282          it != record_->method_end();
283          ++it) {
284       if (it->getNameAsString() == kNewOperatorName) {
285         if (it->getNumParams() == 1) {
286           new_deleted = it->isDeleted();
287         } else if (it->getNumParams() == 2) {
288           placement = !it->isDeleted();
289         }
290       }
291     }
292     is_only_placement_newable_ = (placement && new_deleted) ? kTrue : kFalse;
293   }
294   return is_only_placement_newable_;
295 }
296 
DeclaresNewOperator()297 CXXMethodDecl* RecordInfo::DeclaresNewOperator() {
298   for (CXXRecordDecl::method_iterator it = record_->method_begin();
299        it != record_->method_end();
300        ++it) {
301     if (it->getNameAsString() == kNewOperatorName && it->getNumParams() == 1)
302       return *it;
303   }
304   return 0;
305 }
306 
307 // An object requires a tracing method if it has any fields that need tracing
308 // or if it inherits from multiple bases that need tracing.
RequiresTraceMethod()309 bool RecordInfo::RequiresTraceMethod() {
310   if (IsStackAllocated())
311     return false;
312   unsigned bases_with_trace = 0;
313   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
314     if (it->second.NeedsTracing().IsNeeded())
315       ++bases_with_trace;
316   }
317   if (bases_with_trace > 1)
318     return true;
319   GetFields();
320   return fields_need_tracing_.IsNeeded();
321 }
322 
323 // Get the actual tracing method (ie, can be traceAfterDispatch if there is a
324 // dispatch method).
GetTraceMethod()325 CXXMethodDecl* RecordInfo::GetTraceMethod() {
326   DetermineTracingMethods();
327   return trace_method_;
328 }
329 
330 // Get the static trace dispatch method.
GetTraceDispatchMethod()331 CXXMethodDecl* RecordInfo::GetTraceDispatchMethod() {
332   DetermineTracingMethods();
333   return trace_dispatch_method_;
334 }
335 
GetFinalizeDispatchMethod()336 CXXMethodDecl* RecordInfo::GetFinalizeDispatchMethod() {
337   DetermineTracingMethods();
338   return finalize_dispatch_method_;
339 }
340 
GetBases()341 RecordInfo::Bases& RecordInfo::GetBases() {
342   if (!bases_)
343     bases_ = CollectBases();
344   return *bases_;
345 }
346 
InheritsTrace()347 bool RecordInfo::InheritsTrace() {
348   if (GetTraceMethod())
349     return true;
350   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
351     if (it->second.info()->InheritsTrace())
352       return true;
353   }
354   return false;
355 }
356 
InheritsNonVirtualTrace()357 CXXMethodDecl* RecordInfo::InheritsNonVirtualTrace() {
358   if (CXXMethodDecl* trace = GetTraceMethod())
359     return trace->isVirtual() ? 0 : trace;
360   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
361     if (CXXMethodDecl* trace = it->second.info()->InheritsNonVirtualTrace())
362       return trace;
363   }
364   return 0;
365 }
366 
DeclaresGCMixinMethods()367 bool RecordInfo::DeclaresGCMixinMethods() {
368   DetermineTracingMethods();
369   return has_gc_mixin_methods_;
370 }
371 
DeclaresLocalTraceMethod()372 bool RecordInfo::DeclaresLocalTraceMethod() {
373   if (is_declaring_local_trace_ != kNotComputed)
374     return is_declaring_local_trace_;
375   DetermineTracingMethods();
376   is_declaring_local_trace_ = trace_method_ ? kTrue : kFalse;
377   if (is_declaring_local_trace_) {
378     for (auto it = record_->method_begin();
379          it != record_->method_end(); ++it) {
380       if (*it == trace_method_) {
381         is_declaring_local_trace_ = kTrue;
382         break;
383       }
384     }
385   }
386   return is_declaring_local_trace_;
387 }
388 
389 // A (non-virtual) class is considered abstract in Blink if it has
390 // no public constructors and no create methods.
IsConsideredAbstract()391 bool RecordInfo::IsConsideredAbstract() {
392   for (CXXRecordDecl::ctor_iterator it = record_->ctor_begin();
393        it != record_->ctor_end();
394        ++it) {
395     if (!it->isCopyOrMoveConstructor() && it->getAccess() == AS_public)
396       return false;
397   }
398   for (CXXRecordDecl::method_iterator it = record_->method_begin();
399        it != record_->method_end();
400        ++it) {
401     if (it->getNameAsString() == kCreateName)
402       return false;
403   }
404   return true;
405 }
406 
CollectBases()407 RecordInfo::Bases* RecordInfo::CollectBases() {
408   // Compute the collection locally to avoid inconsistent states.
409   Bases* bases = new Bases;
410   if (!record_->hasDefinition())
411     return bases;
412   for (CXXRecordDecl::base_class_iterator it = record_->bases_begin();
413        it != record_->bases_end();
414        ++it) {
415     const CXXBaseSpecifier& spec = *it;
416     RecordInfo* info = cache_->Lookup(spec.getType());
417     if (!info)
418       continue;
419     CXXRecordDecl* base = info->record();
420     TracingStatus status = info->InheritsTrace()
421                                ? TracingStatus::Needed()
422                                : TracingStatus::Unneeded();
423     bases->push_back(std::make_pair(base, BasePoint(spec, info, status)));
424   }
425   return bases;
426 }
427 
GetFields()428 RecordInfo::Fields& RecordInfo::GetFields() {
429   if (!fields_)
430     fields_ = CollectFields();
431   return *fields_;
432 }
433 
CollectFields()434 RecordInfo::Fields* RecordInfo::CollectFields() {
435   // Compute the collection locally to avoid inconsistent states.
436   Fields* fields = new Fields;
437   if (!record_->hasDefinition())
438     return fields;
439   TracingStatus fields_status = TracingStatus::Unneeded();
440   for (RecordDecl::field_iterator it = record_->field_begin();
441        it != record_->field_end();
442        ++it) {
443     FieldDecl* field = *it;
444     // Ignore fields annotated with the GC_PLUGIN_IGNORE macro.
445     if (Config::IsIgnoreAnnotated(field))
446       continue;
447     // Check if the unexpanded type should be recorded; needed
448     // to track iterator aliases only
449     const Type* unexpandedType = field->getType().getSplitUnqualifiedType().Ty;
450     Edge* edge = CreateEdgeFromOriginalType(unexpandedType);
451     if (!edge)
452       edge = CreateEdge(field->getType().getTypePtrOrNull());
453     if (edge) {
454       fields_status = fields_status.LUB(edge->NeedsTracing(Edge::kRecursive));
455       fields->insert(std::make_pair(field, FieldPoint(field, edge)));
456     }
457   }
458   fields_need_tracing_ = fields_status;
459   return fields;
460 }
461 
DetermineTracingMethods()462 void RecordInfo::DetermineTracingMethods() {
463   if (determined_trace_methods_)
464     return;
465   determined_trace_methods_ = true;
466   if (Config::IsGCBase(name_))
467     return;
468   CXXMethodDecl* trace = nullptr;
469   CXXMethodDecl* trace_after_dispatch = nullptr;
470   bool has_adjust_and_mark = false;
471   bool has_is_heap_object_alive = false;
472   for (Decl* decl : record_->decls()) {
473     CXXMethodDecl* method = dyn_cast<CXXMethodDecl>(decl);
474     if (!method) {
475       if (FunctionTemplateDecl* func_template =
476           dyn_cast<FunctionTemplateDecl>(decl))
477         method = dyn_cast<CXXMethodDecl>(func_template->getTemplatedDecl());
478     }
479     if (!method)
480       continue;
481 
482     switch (Config::GetTraceMethodType(method)) {
483       case Config::TRACE_METHOD:
484         trace = method;
485         break;
486       case Config::TRACE_AFTER_DISPATCH_METHOD:
487         trace_after_dispatch = method;
488         break;
489       case Config::NOT_TRACE_METHOD:
490         if (method->getNameAsString() == kFinalizeName) {
491           finalize_dispatch_method_ = method;
492         } else if (method->getNameAsString() == kAdjustAndMarkName) {
493           has_adjust_and_mark = true;
494         } else if (method->getNameAsString() == kIsHeapObjectAliveName) {
495           has_is_heap_object_alive = true;
496         }
497         break;
498     }
499   }
500 
501   // Record if class defines the two GCMixin methods.
502   has_gc_mixin_methods_ =
503       has_adjust_and_mark && has_is_heap_object_alive ? kTrue : kFalse;
504   if (trace_after_dispatch) {
505     trace_method_ = trace_after_dispatch;
506     trace_dispatch_method_ = trace;
507   } else {
508     // TODO: Can we never have a dispatch method called trace without the same
509     // class defining a traceAfterDispatch method?
510     trace_method_ = trace;
511     trace_dispatch_method_ = nullptr;
512   }
513   if (trace_dispatch_method_ && finalize_dispatch_method_)
514     return;
515   // If this class does not define dispatching methods inherit them.
516   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
517     // TODO: Does it make sense to inherit multiple dispatch methods?
518     if (CXXMethodDecl* dispatch = it->second.info()->GetTraceDispatchMethod()) {
519       assert(!trace_dispatch_method_ && "Multiple trace dispatching methods");
520       trace_dispatch_method_ = dispatch;
521     }
522     if (CXXMethodDecl* dispatch =
523             it->second.info()->GetFinalizeDispatchMethod()) {
524       assert(!finalize_dispatch_method_ &&
525              "Multiple finalize dispatching methods");
526       finalize_dispatch_method_ = dispatch;
527     }
528   }
529 }
530 
531 // TODO: Add classes with a finalize() method that specialize FinalizerTrait.
NeedsFinalization()532 bool RecordInfo::NeedsFinalization() {
533   if (does_need_finalization_ == kNotComputed) {
534     if (HasOptionalFinalizer()) {
535       does_need_finalization_ = kFalse;
536       return does_need_finalization_;
537     }
538 
539     // Rely on hasNonTrivialDestructor(), but if the only
540     // identifiable reason for it being true is the presence
541     // of a safely ignorable class as a direct base,
542     // or we're processing such an 'ignorable' class, then it does
543     // not need finalization.
544     does_need_finalization_ =
545         record_->hasNonTrivialDestructor() ? kTrue : kFalse;
546     if (!does_need_finalization_)
547       return does_need_finalization_;
548 
549     CXXDestructorDecl* dtor = record_->getDestructor();
550     if (dtor && dtor->isUserProvided())
551       return does_need_finalization_;
552     for (Fields::iterator it = GetFields().begin();
553          it != GetFields().end();
554          ++it) {
555       if (it->second.edge()->NeedsFinalization())
556         return does_need_finalization_;
557     }
558 
559     for (Bases::iterator it = GetBases().begin();
560          it != GetBases().end();
561          ++it) {
562       if (it->second.info()->NeedsFinalization())
563         return does_need_finalization_;
564     }
565     // Destructor was non-trivial due to bases with destructors that
566     // can be safely ignored. Hence, no need for finalization.
567     does_need_finalization_ = kFalse;
568   }
569   return does_need_finalization_;
570 }
571 
572 // A class needs tracing if:
573 // - it is allocated on the managed heap,
574 // - it is derived from a class that needs tracing, or
575 // - it contains fields that need tracing.
576 //
NeedsTracing(Edge::NeedsTracingOption option)577 TracingStatus RecordInfo::NeedsTracing(Edge::NeedsTracingOption option) {
578   if (IsGCAllocated())
579     return TracingStatus::Needed();
580 
581   if (IsStackAllocated())
582     return TracingStatus::Unneeded();
583 
584   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
585     if (it->second.info()->NeedsTracing(option).IsNeeded())
586       return TracingStatus::Needed();
587   }
588 
589   if (option == Edge::kRecursive)
590     GetFields();
591 
592   return fields_need_tracing_;
593 }
594 
isInStdNamespace(clang::Sema & sema,NamespaceDecl * ns)595 static bool isInStdNamespace(clang::Sema& sema, NamespaceDecl* ns)
596 {
597   while (ns) {
598     if (sema.getStdNamespace()->InEnclosingNamespaceSetOf(ns))
599       return true;
600     ns = dyn_cast<NamespaceDecl>(ns->getParent());
601   }
602   return false;
603 }
604 
CreateEdgeFromOriginalType(const Type * type)605 Edge* RecordInfo::CreateEdgeFromOriginalType(const Type* type) {
606   if (!type)
607     return nullptr;
608 
609   // look for "typedef ... iterator;"
610   if (!isa<ElaboratedType>(type))
611     return nullptr;
612   const ElaboratedType* elaboratedType = cast<ElaboratedType>(type);
613   if (!isa<TypedefType>(elaboratedType->getNamedType()))
614     return nullptr;
615   const TypedefType* typedefType =
616       cast<TypedefType>(elaboratedType->getNamedType());
617   std::string typeName = typedefType->getDecl()->getNameAsString();
618   if (!Config::IsIterator(typeName))
619     return nullptr;
620   RecordInfo* info =
621       cache_->Lookup(elaboratedType->getQualifier()->getAsType());
622 
623   bool on_heap = false;
624   bool is_unsafe = false;
625   // Silently handle unknown types; the on-heap collection types will
626   // have to be in scope for the declaration to compile, though.
627   if (info) {
628     is_unsafe = Config::IsGCCollectionWithUnsafeIterator(info->name());
629     // Don't mark iterator as being on the heap if it is not supported.
630     on_heap = !is_unsafe && Config::IsGCCollection(info->name());
631   }
632   return new Iterator(info, on_heap, is_unsafe);
633 }
634 
CreateEdge(const Type * type)635 Edge* RecordInfo::CreateEdge(const Type* type) {
636   if (!type) {
637     return 0;
638   }
639 
640   if (type->isPointerType() || type->isReferenceType()) {
641     if (Edge* ptr = CreateEdge(type->getPointeeType().getTypePtrOrNull()))
642       return new RawPtr(ptr, type->isReferenceType());
643     return 0;
644   }
645 
646   RecordInfo* info = cache_->Lookup(type);
647 
648   // If the type is neither a pointer or a C++ record we ignore it.
649   if (!info) {
650     return 0;
651   }
652 
653   TemplateArgs args;
654 
655   if (Config::IsRefPtr(info->name()) && info->GetTemplateArgs(1, &args)) {
656     if (Edge* ptr = CreateEdge(args[0]))
657       return new RefPtr(ptr);
658     return 0;
659   }
660 
661   if (Config::IsUniquePtr(info->name()) && info->GetTemplateArgs(1, &args)) {
662     // Check that this is std::unique_ptr
663     NamespaceDecl* ns =
664         dyn_cast<NamespaceDecl>(info->record()->getDeclContext());
665     clang::Sema& sema = cache_->instance().getSema();
666     if (!isInStdNamespace(sema, ns))
667       return 0;
668     if (Edge* ptr = CreateEdge(args[0]))
669       return new UniquePtr(ptr);
670     return 0;
671   }
672 
673   if (Config::IsMember(info->name()) && info->GetTemplateArgs(1, &args)) {
674     if (Edge* ptr = CreateEdge(args[0]))
675       return new Member(ptr);
676     return 0;
677   }
678 
679   if (Config::IsWeakMember(info->name()) && info->GetTemplateArgs(1, &args)) {
680     if (Edge* ptr = CreateEdge(args[0]))
681       return new WeakMember(ptr);
682     return 0;
683   }
684 
685   bool is_persistent = Config::IsPersistent(info->name());
686   if (is_persistent || Config::IsCrossThreadPersistent(info->name())) {
687     // Persistent might refer to v8::Persistent, so check the name space.
688     // TODO: Consider using a more canonical identification than names.
689     NamespaceDecl* ns =
690         dyn_cast<NamespaceDecl>(info->record()->getDeclContext());
691     if (!ns || ns->getName() != "blink")
692       return 0;
693     if (!info->GetTemplateArgs(1, &args))
694       return 0;
695     if (Edge* ptr = CreateEdge(args[0])) {
696       if (is_persistent)
697         return new Persistent(ptr);
698       else
699         return new CrossThreadPersistent(ptr);
700     }
701     return 0;
702   }
703 
704   if (Config::IsGCCollection(info->name()) ||
705       Config::IsWTFCollection(info->name())) {
706     bool is_root = Config::IsPersistentGCCollection(info->name());
707     bool on_heap = is_root || info->IsHeapAllocatedCollection();
708     size_t count = Config::CollectionDimension(info->name());
709     if (!info->GetTemplateArgs(count, &args))
710       return 0;
711     Collection* edge = new Collection(info, on_heap, is_root);
712     for (TemplateArgs::iterator it = args.begin(); it != args.end(); ++it) {
713       if (Edge* member = CreateEdge(*it)) {
714         edge->members().push_back(member);
715       }
716       // TODO: Handle the case where we fail to create an edge (eg, if the
717       // argument is a primitive type or just not fully known yet).
718     }
719     return edge;
720   }
721 
722   return new Value(info);
723 }
724