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.
6 #include "RecordInfo.h"
11 RecordInfo::RecordInfo(CXXRecordDecl
* record
, RecordCache
* cache
)
14 name_(record
->getName()),
15 fields_need_tracing_(TracingStatus::Unknown()),
18 is_stack_allocated_(kNotComputed
),
19 is_non_newable_(kNotComputed
),
20 is_only_placement_newable_(kNotComputed
),
21 does_need_finalization_(kNotComputed
),
22 has_gc_mixin_methods_(kNotComputed
),
23 is_declaring_local_trace_(kNotComputed
),
24 is_eagerly_finalized_(kNotComputed
),
25 determined_trace_methods_(false),
27 trace_dispatch_method_(0),
28 finalize_dispatch_method_(0),
29 is_gc_derived_(false) {}
31 RecordInfo::~RecordInfo() {
36 // Get |count| number of template arguments. Returns false if there
37 // are fewer than |count| arguments or any of the arguments are not
38 // of a valid Type structure. If |count| is non-positive, all
39 // arguments are collected.
40 bool RecordInfo::GetTemplateArgs(size_t count
, TemplateArgs
* output_args
) {
41 ClassTemplateSpecializationDecl
* tmpl
=
42 dyn_cast
<ClassTemplateSpecializationDecl
>(record_
);
45 const TemplateArgumentList
& args
= tmpl
->getTemplateArgs();
46 if (args
.size() < count
)
50 for (unsigned i
= 0; i
< count
; ++i
) {
51 TemplateArgument arg
= args
[i
];
52 if (arg
.getKind() == TemplateArgument::Type
&& !arg
.getAsType().isNull()) {
53 output_args
->push_back(arg
.getAsType().getTypePtr());
61 // Test if a record is a HeapAllocated collection.
62 bool RecordInfo::IsHeapAllocatedCollection() {
63 if (!Config::IsGCCollection(name_
) && !Config::IsWTFCollection(name_
))
67 if (GetTemplateArgs(0, &args
)) {
68 for (TemplateArgs::iterator it
= args
.begin(); it
!= args
.end(); ++it
) {
69 if (CXXRecordDecl
* decl
= (*it
)->getAsCXXRecordDecl())
70 if (decl
->getName() == kHeapAllocatorName
)
75 return Config::IsGCCollection(name_
);
78 // Test if a record is derived from a garbage collected base.
79 bool RecordInfo::IsGCDerived() {
80 // If already computed, return the known result.
81 if (gc_base_names_
.size())
82 return is_gc_derived_
;
84 if (!record_
->hasDefinition())
87 // The base classes are not themselves considered garbage collected objects.
88 if (Config::IsGCBase(name_
))
91 // Walk the inheritance tree to find GC base classes.
93 return is_gc_derived_
;
96 CXXRecordDecl
* RecordInfo::GetDependentTemplatedDecl(const Type
& type
) {
97 const TemplateSpecializationType
* tmpl_type
=
98 type
.getAs
<TemplateSpecializationType
>();
102 TemplateDecl
* tmpl_decl
= tmpl_type
->getTemplateName().getAsTemplateDecl();
106 return dyn_cast_or_null
<CXXRecordDecl
>(tmpl_decl
->getTemplatedDecl());
109 void RecordInfo::walkBases() {
110 // This traversal is akin to CXXRecordDecl::forallBases()'s,
111 // but without stepping over dependent bases -- these might also
112 // have a "GC base name", so are to be included and considered.
113 SmallVector
<const CXXRecordDecl
*, 8> queue
;
115 const CXXRecordDecl
*base_record
= record();
117 for (const auto& it
: base_record
->bases()) {
118 const RecordType
*type
= it
.getType()->getAs
<RecordType
>();
121 base
= GetDependentTemplatedDecl(*it
.getType());
123 base
= cast_or_null
<CXXRecordDecl
>(type
->getDecl()->getDefinition());
125 queue
.push_back(base
);
130 const std::string
& name
= base
->getName();
131 if (Config::IsGCBase(name
)) {
132 gc_base_names_
.push_back(name
);
133 is_gc_derived_
= true;
139 base_record
= queue
.pop_back_val(); // not actually a queue.
143 bool RecordInfo::IsGCFinalized() {
146 for (const auto& gc_base
: gc_base_names_
) {
147 if (Config::IsGCFinalizedBase(gc_base
))
153 // A GC mixin is a class that inherits from a GC mixin base and has
154 // not yet been "mixed in" with another GC base class.
155 bool RecordInfo::IsGCMixin() {
156 if (!IsGCDerived() || !gc_base_names_
.size())
158 for (const auto& gc_base
: gc_base_names_
) {
159 // If it is not a mixin base we are done.
160 if (!Config::IsGCMixinBase(gc_base
))
163 // This is a mixin if all GC bases are mixins.
167 // Test if a record is allocated on the managed heap.
168 bool RecordInfo::IsGCAllocated() {
169 return IsGCDerived() || IsHeapAllocatedCollection();
172 bool RecordInfo::IsEagerlyFinalized() {
173 if (is_eagerly_finalized_
== kNotComputed
) {
174 is_eagerly_finalized_
= kFalse
;
175 if (IsGCFinalized()) {
176 for (Decl
* decl
: record_
->decls()) {
177 if (TypedefDecl
* typedef_decl
= dyn_cast
<TypedefDecl
>(decl
)) {
178 if (typedef_decl
->getNameAsString() == kIsEagerlyFinalizedName
) {
179 is_eagerly_finalized_
= kTrue
;
186 return is_eagerly_finalized_
;
189 bool RecordInfo::HasDefinition() {
190 return record_
->hasDefinition();
193 RecordInfo
* RecordCache::Lookup(CXXRecordDecl
* record
) {
194 // Ignore classes annotated with the GC_PLUGIN_IGNORE macro.
195 if (!record
|| Config::IsIgnoreAnnotated(record
))
197 Cache::iterator it
= cache_
.find(record
);
198 if (it
!= cache_
.end())
200 return &cache_
.insert(std::make_pair(record
, RecordInfo(record
, this)))
204 bool RecordInfo::IsStackAllocated() {
205 if (is_stack_allocated_
== kNotComputed
) {
206 is_stack_allocated_
= kFalse
;
207 for (Bases::iterator it
= GetBases().begin();
208 it
!= GetBases().end();
210 if (it
->second
.info()->IsStackAllocated()) {
211 is_stack_allocated_
= kTrue
;
212 return is_stack_allocated_
;
215 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
216 it
!= record_
->method_end();
218 if (it
->getNameAsString() == kNewOperatorName
&&
220 Config::IsStackAnnotated(*it
)) {
221 is_stack_allocated_
= kTrue
;
222 return is_stack_allocated_
;
226 return is_stack_allocated_
;
229 bool RecordInfo::IsNonNewable() {
230 if (is_non_newable_
== kNotComputed
) {
231 bool deleted
= false;
232 bool all_deleted
= true;
233 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
234 it
!= record_
->method_end();
236 if (it
->getNameAsString() == kNewOperatorName
) {
237 deleted
= it
->isDeleted();
238 all_deleted
= all_deleted
&& deleted
;
241 is_non_newable_
= (deleted
&& all_deleted
) ? kTrue
: kFalse
;
243 return is_non_newable_
;
246 bool RecordInfo::IsOnlyPlacementNewable() {
247 if (is_only_placement_newable_
== kNotComputed
) {
248 bool placement
= false;
249 bool new_deleted
= false;
250 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
251 it
!= record_
->method_end();
253 if (it
->getNameAsString() == kNewOperatorName
) {
254 if (it
->getNumParams() == 1) {
255 new_deleted
= it
->isDeleted();
256 } else if (it
->getNumParams() == 2) {
257 placement
= !it
->isDeleted();
261 is_only_placement_newable_
= (placement
&& new_deleted
) ? kTrue
: kFalse
;
263 return is_only_placement_newable_
;
266 CXXMethodDecl
* RecordInfo::DeclaresNewOperator() {
267 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
268 it
!= record_
->method_end();
270 if (it
->getNameAsString() == kNewOperatorName
&& it
->getNumParams() == 1)
276 // An object requires a tracing method if it has any fields that need tracing
277 // or if it inherits from multiple bases that need tracing.
278 bool RecordInfo::RequiresTraceMethod() {
279 if (IsStackAllocated())
281 unsigned bases_with_trace
= 0;
282 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
283 if (it
->second
.NeedsTracing().IsNeeded())
286 if (bases_with_trace
> 1)
289 return fields_need_tracing_
.IsNeeded();
292 // Get the actual tracing method (ie, can be traceAfterDispatch if there is a
294 CXXMethodDecl
* RecordInfo::GetTraceMethod() {
295 DetermineTracingMethods();
296 return trace_method_
;
299 // Get the static trace dispatch method.
300 CXXMethodDecl
* RecordInfo::GetTraceDispatchMethod() {
301 DetermineTracingMethods();
302 return trace_dispatch_method_
;
305 CXXMethodDecl
* RecordInfo::GetFinalizeDispatchMethod() {
306 DetermineTracingMethods();
307 return finalize_dispatch_method_
;
310 RecordInfo::Bases
& RecordInfo::GetBases() {
312 bases_
= CollectBases();
316 bool RecordInfo::InheritsTrace() {
317 if (GetTraceMethod())
319 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
320 if (it
->second
.info()->InheritsTrace())
326 CXXMethodDecl
* RecordInfo::InheritsNonVirtualTrace() {
327 if (CXXMethodDecl
* trace
= GetTraceMethod())
328 return trace
->isVirtual() ? 0 : trace
;
329 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
330 if (CXXMethodDecl
* trace
= it
->second
.info()->InheritsNonVirtualTrace())
336 bool RecordInfo::DeclaresGCMixinMethods() {
337 DetermineTracingMethods();
338 return has_gc_mixin_methods_
;
341 bool RecordInfo::DeclaresLocalTraceMethod() {
342 if (is_declaring_local_trace_
!= kNotComputed
)
343 return is_declaring_local_trace_
;
344 DetermineTracingMethods();
345 is_declaring_local_trace_
= trace_method_
? kTrue
: kFalse
;
346 if (is_declaring_local_trace_
) {
347 for (auto it
= record_
->method_begin();
348 it
!= record_
->method_end(); ++it
) {
349 if (*it
== trace_method_
) {
350 is_declaring_local_trace_
= kTrue
;
355 return is_declaring_local_trace_
;
358 bool RecordInfo::IsGCMixinInstance() {
359 assert(IsGCDerived());
360 if (record_
->isAbstract())
363 assert(!IsGCMixin());
365 // true iff the class derives from GCMixin and
366 // one or more other GC base classes.
367 bool seen_gc_mixin
= false;
368 bool seen_gc_derived
= false;
369 for (const auto& gc_base
: gc_base_names_
) {
370 if (Config::IsGCMixinBase(gc_base
))
371 seen_gc_mixin
= true;
372 else if (Config::IsGCBase(gc_base
))
373 seen_gc_derived
= true;
375 return seen_gc_derived
&& seen_gc_mixin
;
378 // A (non-virtual) class is considered abstract in Blink if it has
379 // no public constructors and no create methods.
380 bool RecordInfo::IsConsideredAbstract() {
381 for (CXXRecordDecl::ctor_iterator it
= record_
->ctor_begin();
382 it
!= record_
->ctor_end();
384 if (!it
->isCopyOrMoveConstructor() && it
->getAccess() == AS_public
)
387 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
388 it
!= record_
->method_end();
390 if (it
->getNameAsString() == kCreateName
)
396 RecordInfo::Bases
* RecordInfo::CollectBases() {
397 // Compute the collection locally to avoid inconsistent states.
398 Bases
* bases
= new Bases
;
399 if (!record_
->hasDefinition())
401 for (CXXRecordDecl::base_class_iterator it
= record_
->bases_begin();
402 it
!= record_
->bases_end();
404 const CXXBaseSpecifier
& spec
= *it
;
405 RecordInfo
* info
= cache_
->Lookup(spec
.getType());
408 CXXRecordDecl
* base
= info
->record();
409 TracingStatus status
= info
->InheritsTrace()
410 ? TracingStatus::Needed()
411 : TracingStatus::Unneeded();
412 bases
->insert(std::make_pair(base
, BasePoint(spec
, info
, status
)));
417 RecordInfo::Fields
& RecordInfo::GetFields() {
419 fields_
= CollectFields();
423 RecordInfo::Fields
* RecordInfo::CollectFields() {
424 // Compute the collection locally to avoid inconsistent states.
425 Fields
* fields
= new Fields
;
426 if (!record_
->hasDefinition())
428 TracingStatus fields_status
= TracingStatus::Unneeded();
429 for (RecordDecl::field_iterator it
= record_
->field_begin();
430 it
!= record_
->field_end();
432 FieldDecl
* field
= *it
;
433 // Ignore fields annotated with the GC_PLUGIN_IGNORE macro.
434 if (Config::IsIgnoreAnnotated(field
))
436 if (Edge
* edge
= CreateEdge(field
->getType().getTypePtrOrNull())) {
437 fields_status
= fields_status
.LUB(edge
->NeedsTracing(Edge::kRecursive
));
438 fields
->insert(std::make_pair(field
, FieldPoint(field
, edge
)));
441 fields_need_tracing_
= fields_status
;
445 void RecordInfo::DetermineTracingMethods() {
446 if (determined_trace_methods_
)
448 determined_trace_methods_
= true;
449 if (Config::IsGCBase(name_
))
451 CXXMethodDecl
* trace
= nullptr;
452 CXXMethodDecl
* trace_impl
= nullptr;
453 CXXMethodDecl
* trace_after_dispatch
= nullptr;
454 bool has_adjust_and_mark
= false;
455 bool has_is_heap_object_alive
= false;
456 for (Decl
* decl
: record_
->decls()) {
457 CXXMethodDecl
* method
= dyn_cast
<CXXMethodDecl
>(decl
);
459 if (FunctionTemplateDecl
* func_template
=
460 dyn_cast
<FunctionTemplateDecl
>(decl
))
461 method
= dyn_cast
<CXXMethodDecl
>(func_template
->getTemplatedDecl());
466 switch (Config::GetTraceMethodType(method
)) {
467 case Config::TRACE_METHOD
:
470 case Config::TRACE_AFTER_DISPATCH_METHOD
:
471 trace_after_dispatch
= method
;
473 case Config::TRACE_IMPL_METHOD
:
476 case Config::TRACE_AFTER_DISPATCH_IMPL_METHOD
:
478 case Config::NOT_TRACE_METHOD
:
479 if (method
->getNameAsString() == kFinalizeName
) {
480 finalize_dispatch_method_
= method
;
481 } else if (method
->getNameAsString() == kAdjustAndMarkName
) {
482 has_adjust_and_mark
= true;
483 } else if (method
->getNameAsString() == kIsHeapObjectAliveName
) {
484 has_is_heap_object_alive
= true;
490 // Record if class defines the two GCMixin methods.
491 has_gc_mixin_methods_
=
492 has_adjust_and_mark
&& has_is_heap_object_alive
? kTrue
: kFalse
;
493 if (trace_after_dispatch
) {
494 trace_method_
= trace_after_dispatch
;
495 trace_dispatch_method_
= trace_impl
? trace_impl
: trace
;
497 // TODO: Can we never have a dispatch method called trace without the same
498 // class defining a traceAfterDispatch method?
499 trace_method_
= trace
;
500 trace_dispatch_method_
= nullptr;
502 if (trace_dispatch_method_
&& finalize_dispatch_method_
)
504 // If this class does not define dispatching methods inherit them.
505 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
506 // TODO: Does it make sense to inherit multiple dispatch methods?
507 if (CXXMethodDecl
* dispatch
= it
->second
.info()->GetTraceDispatchMethod()) {
508 assert(!trace_dispatch_method_
&& "Multiple trace dispatching methods");
509 trace_dispatch_method_
= dispatch
;
511 if (CXXMethodDecl
* dispatch
=
512 it
->second
.info()->GetFinalizeDispatchMethod()) {
513 assert(!finalize_dispatch_method_
&&
514 "Multiple finalize dispatching methods");
515 finalize_dispatch_method_
= dispatch
;
520 // TODO: Add classes with a finalize() method that specialize FinalizerTrait.
521 bool RecordInfo::NeedsFinalization() {
522 if (does_need_finalization_
== kNotComputed
) {
523 // Rely on hasNonTrivialDestructor(), but if the only
524 // identifiable reason for it being true is the presence
525 // of a safely ignorable class as a direct base,
526 // or we're processing such an 'ignorable' class, then it does
527 // not need finalization.
528 does_need_finalization_
=
529 record_
->hasNonTrivialDestructor() ? kTrue
: kFalse
;
530 if (!does_need_finalization_
)
531 return does_need_finalization_
;
533 // Processing a class with a safely-ignorable destructor.
535 dyn_cast
<NamespaceDecl
>(record_
->getDeclContext());
536 if (ns
&& Config::HasIgnorableDestructor(ns
->getName(), name_
)) {
537 does_need_finalization_
= kFalse
;
538 return does_need_finalization_
;
541 CXXDestructorDecl
* dtor
= record_
->getDestructor();
542 if (dtor
&& dtor
->isUserProvided())
543 return does_need_finalization_
;
544 for (Fields::iterator it
= GetFields().begin();
545 it
!= GetFields().end();
547 if (it
->second
.edge()->NeedsFinalization())
548 return does_need_finalization_
;
551 for (Bases::iterator it
= GetBases().begin();
552 it
!= GetBases().end();
554 if (it
->second
.info()->NeedsFinalization())
555 return does_need_finalization_
;
557 // Destructor was non-trivial due to bases with destructors that
558 // can be safely ignored. Hence, no need for finalization.
559 does_need_finalization_
= kFalse
;
561 return does_need_finalization_
;
564 // A class needs tracing if:
565 // - it is allocated on the managed heap,
566 // - it is derived from a class that needs tracing, or
567 // - it contains fields that need tracing.
568 // TODO: Defining NeedsTracing based on whether a class defines a trace method
569 // (of the proper signature) over approximates too much. The use of transition
570 // types causes some classes to have trace methods without them needing to be
572 TracingStatus
RecordInfo::NeedsTracing(Edge::NeedsTracingOption option
) {
574 return TracingStatus::Needed();
576 if (IsStackAllocated())
577 return TracingStatus::Unneeded();
579 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
580 if (it
->second
.info()->NeedsTracing(option
).IsNeeded())
581 return TracingStatus::Needed();
584 if (option
== Edge::kRecursive
)
587 return fields_need_tracing_
;
590 Edge
* RecordInfo::CreateEdge(const Type
* type
) {
595 if (type
->isPointerType() || type
->isReferenceType()) {
596 if (Edge
* ptr
= CreateEdge(type
->getPointeeType().getTypePtrOrNull()))
597 return new RawPtr(ptr
, false, type
->isReferenceType());
601 RecordInfo
* info
= cache_
->Lookup(type
);
603 // If the type is neither a pointer or a C++ record we ignore it.
610 if (Config::IsRawPtr(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
611 if (Edge
* ptr
= CreateEdge(args
[0]))
612 return new RawPtr(ptr
, true, false);
616 if (Config::IsRefPtr(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
617 if (Edge
* ptr
= CreateEdge(args
[0]))
618 return new RefPtr(ptr
);
622 if (Config::IsOwnPtr(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
623 if (Edge
* ptr
= CreateEdge(args
[0]))
624 return new OwnPtr(ptr
);
628 if (Config::IsMember(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
629 if (Edge
* ptr
= CreateEdge(args
[0]))
630 return new Member(ptr
);
634 if (Config::IsWeakMember(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
635 if (Edge
* ptr
= CreateEdge(args
[0]))
636 return new WeakMember(ptr
);
640 if (Config::IsPersistent(info
->name())) {
641 // Persistent might refer to v8::Persistent, so check the name space.
642 // TODO: Consider using a more canonical identification than names.
644 dyn_cast
<NamespaceDecl
>(info
->record()->getDeclContext());
645 if (!ns
|| ns
->getName() != "blink")
647 if (!info
->GetTemplateArgs(1, &args
))
649 if (Edge
* ptr
= CreateEdge(args
[0]))
650 return new Persistent(ptr
);
654 if (Config::IsGCCollection(info
->name()) ||
655 Config::IsWTFCollection(info
->name())) {
656 bool is_root
= Config::IsPersistentGCCollection(info
->name());
657 bool on_heap
= is_root
|| info
->IsHeapAllocatedCollection();
658 size_t count
= Config::CollectionDimension(info
->name());
659 if (!info
->GetTemplateArgs(count
, &args
))
661 Collection
* edge
= new Collection(info
, on_heap
, is_root
);
662 for (TemplateArgs::iterator it
= args
.begin(); it
!= args
.end(); ++it
) {
663 if (Edge
* member
= CreateEdge(*it
)) {
664 edge
->members().push_back(member
);
666 // TODO: Handle the case where we fail to create an edge (eg, if the
667 // argument is a primitive type or just not fully known yet).
672 return new Value(info
);