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