Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / tools / clang / blink_gc_plugin / RecordInfo.cpp
blobbda33fa992fb5cd92a907fcbb8d9b1c18c94a61b
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.
5 #include "Config.h"
6 #include "RecordInfo.h"
8 using namespace clang;
9 using std::string;
11 RecordInfo::RecordInfo(CXXRecordDecl* record, RecordCache* cache)
12 : cache_(cache),
13 record_(record),
14 name_(record->getName()),
15 fields_need_tracing_(TracingStatus::Unknown()),
16 bases_(0),
17 fields_(0),
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),
25 trace_method_(0),
26 trace_dispatch_method_(0),
27 finalize_dispatch_method_(0),
28 is_gc_derived_(false) {}
30 RecordInfo::~RecordInfo() {
31 delete fields_;
32 delete bases_;
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_);
42 if (!tmpl)
43 return false;
44 const TemplateArgumentList& args = tmpl->getTemplateArgs();
45 if (args.size() < count)
46 return false;
47 if (count <= 0)
48 count = args.size();
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());
53 } else {
54 return false;
57 return true;
60 // Test if a record is a HeapAllocated collection.
61 bool RecordInfo::IsHeapAllocatedCollection() {
62 if (!Config::IsGCCollection(name_) && !Config::IsWTFCollection(name_))
63 return false;
65 TemplateArgs args;
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)
70 return true;
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())
84 return false;
86 // The base classes are not themselves considered garbage collected objects.
87 if (Config::IsGCBase(name_))
88 return false;
90 // Walk the inheritance tree to find GC base classes.
91 walkBases();
92 return is_gc_derived_;
95 CXXRecordDecl* RecordInfo::GetDependentTemplatedDecl(const Type& type) {
96 const TemplateSpecializationType* tmpl_type =
97 type.getAs<TemplateSpecializationType>();
98 if (!tmpl_type)
99 return 0;
101 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl();
102 if (!tmpl_decl)
103 return 0;
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();
115 while (true) {
116 for (const auto& it : base_record->bases()) {
117 const RecordType *type = it.getType()->getAs<RecordType>();
118 CXXRecordDecl* base;
119 if (!type)
120 base = GetDependentTemplatedDecl(*it.getType());
121 else {
122 base = cast_or_null<CXXRecordDecl>(type->getDecl()->getDefinition());
123 if (base)
124 queue.push_back(base);
126 if (!base)
127 continue;
129 const std::string& name = base->getName();
130 if (Config::IsGCBase(name)) {
131 gc_base_names_.push_back(name);
132 is_gc_derived_ = true;
136 if (queue.empty())
137 break;
138 base_record = queue.pop_back_val(); // not actually a queue.
142 bool RecordInfo::IsGCFinalized() {
143 if (!IsGCDerived())
144 return false;
145 for (const auto& gc_base : gc_base_names_) {
146 if (Config::IsGCFinalizedBase(gc_base))
147 return true;
149 return false;
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())
156 return false;
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))
160 return false;
162 // This is a mixin if all GC bases are mixins.
163 return true;
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))
174 return 0;
175 Cache::iterator it = cache_.find(record);
176 if (it != cache_.end())
177 return &it->second;
178 return &cache_.insert(std::make_pair(record, RecordInfo(record, this)))
179 .first->second;
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();
187 ++it) {
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();
195 ++it) {
196 if (it->getNameAsString() == kNewOperatorName &&
197 it->isDeleted() &&
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();
213 ++it) {
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();
230 ++it) {
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();
247 ++it) {
248 if (it->getNameAsString() == kNewOperatorName && it->getNumParams() == 1)
249 return *it;
251 return 0;
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())
258 return false;
259 unsigned bases_with_trace = 0;
260 for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
261 if (it->second.NeedsTracing().IsNeeded())
262 ++bases_with_trace;
264 if (bases_with_trace > 1)
265 return true;
266 GetFields();
267 return fields_need_tracing_.IsNeeded();
270 // Get the actual tracing method (ie, can be traceAfterDispatch if there is a
271 // dispatch method).
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() {
289 if (!bases_)
290 bases_ = CollectBases();
291 return *bases_;
294 bool RecordInfo::InheritsTrace() {
295 if (GetTraceMethod())
296 return true;
297 for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
298 if (it->second.info()->InheritsTrace())
299 return true;
301 return false;
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())
309 return trace;
311 return 0;
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;
329 break;
333 return is_declaring_local_trace_;
336 bool RecordInfo::IsGCMixinInstance() {
337 assert(IsGCDerived());
338 if (record_->isAbstract())
339 return false;
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();
361 ++it) {
362 if (!it->isCopyOrMoveConstructor() && it->getAccess() == AS_public)
363 return false;
365 for (CXXRecordDecl::method_iterator it = record_->method_begin();
366 it != record_->method_end();
367 ++it) {
368 if (it->getNameAsString() == kCreateName)
369 return false;
371 return true;
374 RecordInfo::Bases* RecordInfo::CollectBases() {
375 // Compute the collection locally to avoid inconsistent states.
376 Bases* bases = new Bases;
377 if (!record_->hasDefinition())
378 return bases;
379 for (CXXRecordDecl::base_class_iterator it = record_->bases_begin();
380 it != record_->bases_end();
381 ++it) {
382 const CXXBaseSpecifier& spec = *it;
383 RecordInfo* info = cache_->Lookup(spec.getType());
384 if (!info)
385 continue;
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)));
392 return bases;
395 RecordInfo::Fields& RecordInfo::GetFields() {
396 if (!fields_)
397 fields_ = CollectFields();
398 return *fields_;
401 RecordInfo::Fields* RecordInfo::CollectFields() {
402 // Compute the collection locally to avoid inconsistent states.
403 Fields* fields = new Fields;
404 if (!record_->hasDefinition())
405 return fields;
406 TracingStatus fields_status = TracingStatus::Unneeded();
407 for (RecordDecl::field_iterator it = record_->field_begin();
408 it != record_->field_end();
409 ++it) {
410 FieldDecl* field = *it;
411 // Ignore fields annotated with the GC_PLUGIN_IGNORE macro.
412 if (Config::IsIgnoreAnnotated(field))
413 continue;
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;
420 return fields;
423 void RecordInfo::DetermineTracingMethods() {
424 if (determined_trace_methods_)
425 return;
426 determined_trace_methods_ = true;
427 if (Config::IsGCBase(name_))
428 return;
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);
436 if (!method) {
437 if (FunctionTemplateDecl* func_template =
438 dyn_cast<FunctionTemplateDecl>(decl))
439 method = dyn_cast<CXXMethodDecl>(func_template->getTemplatedDecl());
441 if (!method)
442 continue;
444 switch (Config::GetTraceMethodType(method)) {
445 case Config::TRACE_METHOD:
446 trace = method;
447 break;
448 case Config::TRACE_AFTER_DISPATCH_METHOD:
449 trace_after_dispatch = method;
450 break;
451 case Config::TRACE_IMPL_METHOD:
452 trace_impl = method;
453 break;
454 case Config::TRACE_AFTER_DISPATCH_IMPL_METHOD:
455 break;
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;
464 break;
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;
474 } else {
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_)
481 return;
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.
512 NamespaceDecl* ns =
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();
524 ++it) {
525 if (it->second.edge()->NeedsFinalization())
526 return does_need_finalization_;
529 for (Bases::iterator it = GetBases().begin();
530 it != GetBases().end();
531 ++it) {
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
549 // traced.
550 TracingStatus RecordInfo::NeedsTracing(Edge::NeedsTracingOption option) {
551 if (IsGCAllocated())
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)
563 GetFields();
565 return fields_need_tracing_;
568 Edge* RecordInfo::CreateEdge(const Type* type) {
569 if (!type) {
570 return 0;
573 if (type->isPointerType()) {
574 if (Edge* ptr = CreateEdge(type->getPointeeType().getTypePtrOrNull()))
575 return new RawPtr(ptr, false);
576 return 0;
579 RecordInfo* info = cache_->Lookup(type);
581 // If the type is neither a pointer or a C++ record we ignore it.
582 if (!info) {
583 return 0;
586 TemplateArgs args;
588 if (Config::IsRawPtr(info->name()) && info->GetTemplateArgs(1, &args)) {
589 if (Edge* ptr = CreateEdge(args[0]))
590 return new RawPtr(ptr, true);
591 return 0;
594 if (Config::IsRefPtr(info->name()) && info->GetTemplateArgs(1, &args)) {
595 if (Edge* ptr = CreateEdge(args[0]))
596 return new RefPtr(ptr);
597 return 0;
600 if (Config::IsOwnPtr(info->name()) && info->GetTemplateArgs(1, &args)) {
601 if (Edge* ptr = CreateEdge(args[0]))
602 return new OwnPtr(ptr);
603 return 0;
606 if (Config::IsMember(info->name()) && info->GetTemplateArgs(1, &args)) {
607 if (Edge* ptr = CreateEdge(args[0]))
608 return new Member(ptr);
609 return 0;
612 if (Config::IsWeakMember(info->name()) && info->GetTemplateArgs(1, &args)) {
613 if (Edge* ptr = CreateEdge(args[0]))
614 return new WeakMember(ptr);
615 return 0;
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.
621 NamespaceDecl* ns =
622 dyn_cast<NamespaceDecl>(info->record()->getDeclContext());
623 if (!ns || ns->getName() != "blink")
624 return 0;
625 if (!info->GetTemplateArgs(1, &args))
626 return 0;
627 if (Edge* ptr = CreateEdge(args[0]))
628 return new Persistent(ptr);
629 return 0;
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))
638 return 0;
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).
647 return edge;
650 return new Value(info);