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 determined_trace_methods_(false),
26 trace_dispatch_method_(0),
27 finalize_dispatch_method_(0),
28 is_gc_derived_(false) {}
30 RecordInfo::~RecordInfo() {
35 // Get |count| number of template arguments. Returns false if there
36 // are fewer than |count| arguments or any of the arguments are not
37 // of a valid Type structure. If |count| is non-positive, all
38 // arguments are collected.
39 bool RecordInfo::GetTemplateArgs(size_t count
, TemplateArgs
* output_args
) {
40 ClassTemplateSpecializationDecl
* tmpl
=
41 dyn_cast
<ClassTemplateSpecializationDecl
>(record_
);
44 const TemplateArgumentList
& args
= tmpl
->getTemplateArgs();
45 if (args
.size() < count
)
49 for (unsigned i
= 0; i
< count
; ++i
) {
50 TemplateArgument arg
= args
[i
];
51 if (arg
.getKind() == TemplateArgument::Type
&& !arg
.getAsType().isNull()) {
52 output_args
->push_back(arg
.getAsType().getTypePtr());
60 // Test if a record is a HeapAllocated collection.
61 bool RecordInfo::IsHeapAllocatedCollection() {
62 if (!Config::IsGCCollection(name_
) && !Config::IsWTFCollection(name_
))
66 if (GetTemplateArgs(0, &args
)) {
67 for (TemplateArgs::iterator it
= args
.begin(); it
!= args
.end(); ++it
) {
68 if (CXXRecordDecl
* decl
= (*it
)->getAsCXXRecordDecl())
69 if (decl
->getName() == kHeapAllocatorName
)
74 return Config::IsGCCollection(name_
);
77 // Test if a record is derived from a garbage collected base.
78 bool RecordInfo::IsGCDerived() {
79 // If already computed, return the known result.
80 if (gc_base_names_
.size())
81 return is_gc_derived_
;
83 if (!record_
->hasDefinition())
86 // The base classes are not themselves considered garbage collected objects.
87 if (Config::IsGCBase(name_
))
90 // Walk the inheritance tree to find GC base classes.
92 return is_gc_derived_
;
95 CXXRecordDecl
* RecordInfo::GetDependentTemplatedDecl(const Type
& type
) {
96 const TemplateSpecializationType
* tmpl_type
=
97 type
.getAs
<TemplateSpecializationType
>();
101 TemplateDecl
* tmpl_decl
= tmpl_type
->getTemplateName().getAsTemplateDecl();
105 return dyn_cast_or_null
<CXXRecordDecl
>(tmpl_decl
->getTemplatedDecl());
108 void RecordInfo::walkBases() {
109 // This traversal is akin to CXXRecordDecl::forallBases()'s,
110 // but without stepping over dependent bases -- these might also
111 // have a "GC base name", so are to be included and considered.
112 SmallVector
<const CXXRecordDecl
*, 8> queue
;
114 const CXXRecordDecl
*base_record
= record();
116 for (const auto& it
: base_record
->bases()) {
117 const RecordType
*type
= it
.getType()->getAs
<RecordType
>();
120 base
= GetDependentTemplatedDecl(*it
.getType());
122 base
= cast_or_null
<CXXRecordDecl
>(type
->getDecl()->getDefinition());
124 queue
.push_back(base
);
129 const std::string
& name
= base
->getName();
130 if (Config::IsGCBase(name
)) {
131 gc_base_names_
.push_back(name
);
132 is_gc_derived_
= true;
138 base_record
= queue
.pop_back_val(); // not actually a queue.
142 bool RecordInfo::IsGCFinalized() {
145 for (const auto& gc_base
: gc_base_names_
) {
146 if (Config::IsGCFinalizedBase(gc_base
))
152 // A GC mixin is a class that inherits from a GC mixin base and has
153 // not yet been "mixed in" with another GC base class.
154 bool RecordInfo::IsGCMixin() {
155 if (!IsGCDerived() || !gc_base_names_
.size())
157 for (const auto& gc_base
: gc_base_names_
) {
158 // If it is not a mixin base we are done.
159 if (!Config::IsGCMixinBase(gc_base
))
162 // This is a mixin if all GC bases are mixins.
166 // Test if a record is allocated on the managed heap.
167 bool RecordInfo::IsGCAllocated() {
168 return IsGCDerived() || IsHeapAllocatedCollection();
171 RecordInfo
* RecordCache::Lookup(CXXRecordDecl
* record
) {
172 // Ignore classes annotated with the GC_PLUGIN_IGNORE macro.
173 if (!record
|| Config::IsIgnoreAnnotated(record
))
175 Cache::iterator it
= cache_
.find(record
);
176 if (it
!= cache_
.end())
178 return &cache_
.insert(std::make_pair(record
, RecordInfo(record
, this)))
182 bool RecordInfo::IsStackAllocated() {
183 if (is_stack_allocated_
== kNotComputed
) {
184 is_stack_allocated_
= kFalse
;
185 for (Bases::iterator it
= GetBases().begin();
186 it
!= GetBases().end();
188 if (it
->second
.info()->IsStackAllocated()) {
189 is_stack_allocated_
= kTrue
;
190 return is_stack_allocated_
;
193 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
194 it
!= record_
->method_end();
196 if (it
->getNameAsString() == kNewOperatorName
&&
198 Config::IsStackAnnotated(*it
)) {
199 is_stack_allocated_
= kTrue
;
200 return is_stack_allocated_
;
204 return is_stack_allocated_
;
207 bool RecordInfo::IsNonNewable() {
208 if (is_non_newable_
== kNotComputed
) {
209 bool deleted
= false;
210 bool all_deleted
= true;
211 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
212 it
!= record_
->method_end();
214 if (it
->getNameAsString() == kNewOperatorName
) {
215 deleted
= it
->isDeleted();
216 all_deleted
= all_deleted
&& deleted
;
219 is_non_newable_
= (deleted
&& all_deleted
) ? kTrue
: kFalse
;
221 return is_non_newable_
;
224 bool RecordInfo::IsOnlyPlacementNewable() {
225 if (is_only_placement_newable_
== kNotComputed
) {
226 bool placement
= false;
227 bool new_deleted
= false;
228 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
229 it
!= record_
->method_end();
231 if (it
->getNameAsString() == kNewOperatorName
) {
232 if (it
->getNumParams() == 1) {
233 new_deleted
= it
->isDeleted();
234 } else if (it
->getNumParams() == 2) {
235 placement
= !it
->isDeleted();
239 is_only_placement_newable_
= (placement
&& new_deleted
) ? kTrue
: kFalse
;
241 return is_only_placement_newable_
;
244 CXXMethodDecl
* RecordInfo::DeclaresNewOperator() {
245 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
246 it
!= record_
->method_end();
248 if (it
->getNameAsString() == kNewOperatorName
&& it
->getNumParams() == 1)
254 // An object requires a tracing method if it has any fields that need tracing
255 // or if it inherits from multiple bases that need tracing.
256 bool RecordInfo::RequiresTraceMethod() {
257 if (IsStackAllocated())
259 unsigned bases_with_trace
= 0;
260 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
261 if (it
->second
.NeedsTracing().IsNeeded())
264 if (bases_with_trace
> 1)
267 return fields_need_tracing_
.IsNeeded();
270 // Get the actual tracing method (ie, can be traceAfterDispatch if there is a
272 CXXMethodDecl
* RecordInfo::GetTraceMethod() {
273 DetermineTracingMethods();
274 return trace_method_
;
277 // Get the static trace dispatch method.
278 CXXMethodDecl
* RecordInfo::GetTraceDispatchMethod() {
279 DetermineTracingMethods();
280 return trace_dispatch_method_
;
283 CXXMethodDecl
* RecordInfo::GetFinalizeDispatchMethod() {
284 DetermineTracingMethods();
285 return finalize_dispatch_method_
;
288 RecordInfo::Bases
& RecordInfo::GetBases() {
290 bases_
= CollectBases();
294 bool RecordInfo::InheritsTrace() {
295 if (GetTraceMethod())
297 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
298 if (it
->second
.info()->InheritsTrace())
304 CXXMethodDecl
* RecordInfo::InheritsNonVirtualTrace() {
305 if (CXXMethodDecl
* trace
= GetTraceMethod())
306 return trace
->isVirtual() ? 0 : trace
;
307 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
308 if (CXXMethodDecl
* trace
= it
->second
.info()->InheritsNonVirtualTrace())
314 bool RecordInfo::DeclaresGCMixinMethods() {
315 DetermineTracingMethods();
316 return has_gc_mixin_methods_
;
319 bool RecordInfo::DeclaresLocalTraceMethod() {
320 if (is_declaring_local_trace_
!= kNotComputed
)
321 return is_declaring_local_trace_
;
322 DetermineTracingMethods();
323 is_declaring_local_trace_
= trace_method_
? kTrue
: kFalse
;
324 if (is_declaring_local_trace_
) {
325 for (auto it
= record_
->method_begin();
326 it
!= record_
->method_end(); ++it
) {
327 if (*it
== trace_method_
) {
328 is_declaring_local_trace_
= kTrue
;
333 return is_declaring_local_trace_
;
336 bool RecordInfo::IsGCMixinInstance() {
337 assert(IsGCDerived());
338 if (record_
->isAbstract())
341 assert(!IsGCMixin());
343 // true iff the class derives from GCMixin and
344 // one or more other GC base classes.
345 bool seen_gc_mixin
= false;
346 bool seen_gc_derived
= false;
347 for (const auto& gc_base
: gc_base_names_
) {
348 if (Config::IsGCMixinBase(gc_base
))
349 seen_gc_mixin
= true;
350 else if (Config::IsGCBase(gc_base
))
351 seen_gc_derived
= true;
353 return seen_gc_derived
&& seen_gc_mixin
;
356 // A (non-virtual) class is considered abstract in Blink if it has
357 // no public constructors and no create methods.
358 bool RecordInfo::IsConsideredAbstract() {
359 for (CXXRecordDecl::ctor_iterator it
= record_
->ctor_begin();
360 it
!= record_
->ctor_end();
362 if (!it
->isCopyOrMoveConstructor() && it
->getAccess() == AS_public
)
365 for (CXXRecordDecl::method_iterator it
= record_
->method_begin();
366 it
!= record_
->method_end();
368 if (it
->getNameAsString() == kCreateName
)
374 RecordInfo::Bases
* RecordInfo::CollectBases() {
375 // Compute the collection locally to avoid inconsistent states.
376 Bases
* bases
= new Bases
;
377 if (!record_
->hasDefinition())
379 for (CXXRecordDecl::base_class_iterator it
= record_
->bases_begin();
380 it
!= record_
->bases_end();
382 const CXXBaseSpecifier
& spec
= *it
;
383 RecordInfo
* info
= cache_
->Lookup(spec
.getType());
386 CXXRecordDecl
* base
= info
->record();
387 TracingStatus status
= info
->InheritsTrace()
388 ? TracingStatus::Needed()
389 : TracingStatus::Unneeded();
390 bases
->insert(std::make_pair(base
, BasePoint(spec
, info
, status
)));
395 RecordInfo::Fields
& RecordInfo::GetFields() {
397 fields_
= CollectFields();
401 RecordInfo::Fields
* RecordInfo::CollectFields() {
402 // Compute the collection locally to avoid inconsistent states.
403 Fields
* fields
= new Fields
;
404 if (!record_
->hasDefinition())
406 TracingStatus fields_status
= TracingStatus::Unneeded();
407 for (RecordDecl::field_iterator it
= record_
->field_begin();
408 it
!= record_
->field_end();
410 FieldDecl
* field
= *it
;
411 // Ignore fields annotated with the GC_PLUGIN_IGNORE macro.
412 if (Config::IsIgnoreAnnotated(field
))
414 if (Edge
* edge
= CreateEdge(field
->getType().getTypePtrOrNull())) {
415 fields_status
= fields_status
.LUB(edge
->NeedsTracing(Edge::kRecursive
));
416 fields
->insert(std::make_pair(field
, FieldPoint(field
, edge
)));
419 fields_need_tracing_
= fields_status
;
423 void RecordInfo::DetermineTracingMethods() {
424 if (determined_trace_methods_
)
426 determined_trace_methods_
= true;
427 if (Config::IsGCBase(name_
))
429 CXXMethodDecl
* trace
= nullptr;
430 CXXMethodDecl
* trace_impl
= nullptr;
431 CXXMethodDecl
* trace_after_dispatch
= nullptr;
432 bool has_adjust_and_mark
= false;
433 bool has_is_heap_object_alive
= false;
434 for (Decl
* decl
: record_
->decls()) {
435 CXXMethodDecl
* method
= dyn_cast
<CXXMethodDecl
>(decl
);
437 if (FunctionTemplateDecl
* func_template
=
438 dyn_cast
<FunctionTemplateDecl
>(decl
))
439 method
= dyn_cast
<CXXMethodDecl
>(func_template
->getTemplatedDecl());
444 switch (Config::GetTraceMethodType(method
)) {
445 case Config::TRACE_METHOD
:
448 case Config::TRACE_AFTER_DISPATCH_METHOD
:
449 trace_after_dispatch
= method
;
451 case Config::TRACE_IMPL_METHOD
:
454 case Config::TRACE_AFTER_DISPATCH_IMPL_METHOD
:
456 case Config::NOT_TRACE_METHOD
:
457 if (method
->getNameAsString() == kFinalizeName
) {
458 finalize_dispatch_method_
= method
;
459 } else if (method
->getNameAsString() == kAdjustAndMarkName
) {
460 has_adjust_and_mark
= true;
461 } else if (method
->getNameAsString() == kIsHeapObjectAliveName
) {
462 has_is_heap_object_alive
= true;
468 // Record if class defines the two GCMixin methods.
469 has_gc_mixin_methods_
=
470 has_adjust_and_mark
&& has_is_heap_object_alive
? kTrue
: kFalse
;
471 if (trace_after_dispatch
) {
472 trace_method_
= trace_after_dispatch
;
473 trace_dispatch_method_
= trace_impl
? trace_impl
: trace
;
475 // TODO: Can we never have a dispatch method called trace without the same
476 // class defining a traceAfterDispatch method?
477 trace_method_
= trace
;
478 trace_dispatch_method_
= nullptr;
480 if (trace_dispatch_method_
&& finalize_dispatch_method_
)
482 // If this class does not define dispatching methods inherit them.
483 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
484 // TODO: Does it make sense to inherit multiple dispatch methods?
485 if (CXXMethodDecl
* dispatch
= it
->second
.info()->GetTraceDispatchMethod()) {
486 assert(!trace_dispatch_method_
&& "Multiple trace dispatching methods");
487 trace_dispatch_method_
= dispatch
;
489 if (CXXMethodDecl
* dispatch
=
490 it
->second
.info()->GetFinalizeDispatchMethod()) {
491 assert(!finalize_dispatch_method_
&&
492 "Multiple finalize dispatching methods");
493 finalize_dispatch_method_
= dispatch
;
498 // TODO: Add classes with a finalize() method that specialize FinalizerTrait.
499 bool RecordInfo::NeedsFinalization() {
500 if (does_need_finalization_
== kNotComputed
) {
501 // Rely on hasNonTrivialDestructor(), but if the only
502 // identifiable reason for it being true is the presence
503 // of a safely ignorable class as a direct base,
504 // or we're processing such an 'ignorable' class, then it does
505 // not need finalization.
506 does_need_finalization_
=
507 record_
->hasNonTrivialDestructor() ? kTrue
: kFalse
;
508 if (!does_need_finalization_
)
509 return does_need_finalization_
;
511 // Processing a class with a safely-ignorable destructor.
513 dyn_cast
<NamespaceDecl
>(record_
->getDeclContext());
514 if (ns
&& Config::HasIgnorableDestructor(ns
->getName(), name_
)) {
515 does_need_finalization_
= kFalse
;
516 return does_need_finalization_
;
519 CXXDestructorDecl
* dtor
= record_
->getDestructor();
520 if (dtor
&& dtor
->isUserProvided())
521 return does_need_finalization_
;
522 for (Fields::iterator it
= GetFields().begin();
523 it
!= GetFields().end();
525 if (it
->second
.edge()->NeedsFinalization())
526 return does_need_finalization_
;
529 for (Bases::iterator it
= GetBases().begin();
530 it
!= GetBases().end();
532 if (it
->second
.info()->NeedsFinalization())
533 return does_need_finalization_
;
535 // Destructor was non-trivial due to bases with destructors that
536 // can be safely ignored. Hence, no need for finalization.
537 does_need_finalization_
= kFalse
;
539 return does_need_finalization_
;
542 // A class needs tracing if:
543 // - it is allocated on the managed heap,
544 // - it is derived from a class that needs tracing, or
545 // - it contains fields that need tracing.
546 // TODO: Defining NeedsTracing based on whether a class defines a trace method
547 // (of the proper signature) over approximates too much. The use of transition
548 // types causes some classes to have trace methods without them needing to be
550 TracingStatus
RecordInfo::NeedsTracing(Edge::NeedsTracingOption option
) {
552 return TracingStatus::Needed();
554 if (IsStackAllocated())
555 return TracingStatus::Unneeded();
557 for (Bases::iterator it
= GetBases().begin(); it
!= GetBases().end(); ++it
) {
558 if (it
->second
.info()->NeedsTracing(option
).IsNeeded())
559 return TracingStatus::Needed();
562 if (option
== Edge::kRecursive
)
565 return fields_need_tracing_
;
568 Edge
* RecordInfo::CreateEdge(const Type
* type
) {
573 if (type
->isPointerType()) {
574 if (Edge
* ptr
= CreateEdge(type
->getPointeeType().getTypePtrOrNull()))
575 return new RawPtr(ptr
, false);
579 RecordInfo
* info
= cache_
->Lookup(type
);
581 // If the type is neither a pointer or a C++ record we ignore it.
588 if (Config::IsRawPtr(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
589 if (Edge
* ptr
= CreateEdge(args
[0]))
590 return new RawPtr(ptr
, true);
594 if (Config::IsRefPtr(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
595 if (Edge
* ptr
= CreateEdge(args
[0]))
596 return new RefPtr(ptr
);
600 if (Config::IsOwnPtr(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
601 if (Edge
* ptr
= CreateEdge(args
[0]))
602 return new OwnPtr(ptr
);
606 if (Config::IsMember(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
607 if (Edge
* ptr
= CreateEdge(args
[0]))
608 return new Member(ptr
);
612 if (Config::IsWeakMember(info
->name()) && info
->GetTemplateArgs(1, &args
)) {
613 if (Edge
* ptr
= CreateEdge(args
[0]))
614 return new WeakMember(ptr
);
618 if (Config::IsPersistent(info
->name())) {
619 // Persistent might refer to v8::Persistent, so check the name space.
620 // TODO: Consider using a more canonical identification than names.
622 dyn_cast
<NamespaceDecl
>(info
->record()->getDeclContext());
623 if (!ns
|| ns
->getName() != "blink")
625 if (!info
->GetTemplateArgs(1, &args
))
627 if (Edge
* ptr
= CreateEdge(args
[0]))
628 return new Persistent(ptr
);
632 if (Config::IsGCCollection(info
->name()) ||
633 Config::IsWTFCollection(info
->name())) {
634 bool is_root
= Config::IsPersistentGCCollection(info
->name());
635 bool on_heap
= is_root
|| info
->IsHeapAllocatedCollection();
636 size_t count
= Config::CollectionDimension(info
->name());
637 if (!info
->GetTemplateArgs(count
, &args
))
639 Collection
* edge
= new Collection(info
, on_heap
, is_root
);
640 for (TemplateArgs::iterator it
= args
.begin(); it
!= args
.end(); ++it
) {
641 if (Edge
* member
= CreateEdge(*it
)) {
642 edge
->members().push_back(member
);
644 // TODO: Handle the case where we fail to create an edge (eg, if the
645 // argument is a primitive type or just not fully known yet).
650 return new Value(info
);