Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / tools / clang / blink_gc_plugin / RecordInfo.cpp
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
8 using namespace clang;
9 using std::string;
10
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       determined_trace_methods_(false),
23       trace_method_(0),
24       trace_dispatch_method_(0),
25       finalize_dispatch_method_(0),
26       is_gc_derived_(false),
27       base_paths_(0) {}
28
29 RecordInfo::~RecordInfo() {
30   delete fields_;
31   delete bases_;
32   delete base_paths_;
33 }
34
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;
55     }
56   }
57   return true;
58 }
59
60 // Test if a record is a HeapAllocated collection.
61 bool RecordInfo::IsHeapAllocatedCollection() {
62   if (!Config::IsGCCollection(name_) && !Config::IsWTFCollection(name_))
63     return false;
64
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;
71     }
72   }
73
74   return Config::IsGCCollection(name_);
75 }
76
77 static bool IsGCBaseCallback(const CXXBaseSpecifier* specifier,
78                              CXXBasePath& path,
79                              void* data) {
80   if (CXXRecordDecl* record = specifier->getType()->getAsCXXRecordDecl())
81     return Config::IsGCBase(record->getName());
82   return false;
83 }
84
85 // Test if a record is derived from a garbage collected base.
86 bool RecordInfo::IsGCDerived() {
87   // If already computed, return the known result.
88   if (base_paths_)
89     return is_gc_derived_;
90
91   base_paths_ = new CXXBasePaths(true, true, false);
92
93   if (!record_->hasDefinition())
94     return false;
95
96   // The base classes are not themselves considered garbage collected objects.
97   if (Config::IsGCBase(name_))
98     return false;
99
100   // Walk the inheritance tree to find GC base classes.
101   is_gc_derived_ = record_->lookupInBases(IsGCBaseCallback, 0, *base_paths_);
102   return is_gc_derived_;
103 }
104
105 bool RecordInfo::IsGCFinalized() {
106   if (!IsGCDerived())
107     return false;
108   for (CXXBasePaths::paths_iterator it = base_paths_->begin();
109        it != base_paths_->end();
110        ++it) {
111     const CXXBasePathElement& elem = (*it)[it->size() - 1];
112     CXXRecordDecl* base = elem.Base->getType()->getAsCXXRecordDecl();
113     if (Config::IsGCFinalizedBase(base->getName()))
114       return true;
115   }
116   return false;
117 }
118
119 // A GC mixin is a class that inherits from a GC mixin base and has
120 // not yet been "mixed in" with another GC base class.
121 bool RecordInfo::IsGCMixin() {
122   if (!IsGCDerived() || base_paths_->begin() == base_paths_->end())
123     return false;
124   for (CXXBasePaths::paths_iterator it = base_paths_->begin();
125        it != base_paths_->end();
126        ++it) {
127       // Get the last element of the path.
128       const CXXBasePathElement& elem = (*it)[it->size() - 1];
129       CXXRecordDecl* base = elem.Base->getType()->getAsCXXRecordDecl();
130       // If it is not a mixin base we are done.
131       if (!Config::IsGCMixinBase(base->getName()))
132           return false;
133   }
134   // This is a mixin if all GC bases are mixins.
135   return true;
136 }
137
138 // Test if a record is allocated on the managed heap.
139 bool RecordInfo::IsGCAllocated() {
140   return IsGCDerived() || IsHeapAllocatedCollection();
141 }
142
143 RecordInfo* RecordCache::Lookup(CXXRecordDecl* record) {
144   // Ignore classes annotated with the GC_PLUGIN_IGNORE macro.
145   if (!record || Config::IsIgnoreAnnotated(record))
146     return 0;
147   Cache::iterator it = cache_.find(record);
148   if (it != cache_.end())
149     return &it->second;
150   return &cache_.insert(std::make_pair(record, RecordInfo(record, this)))
151               .first->second;
152 }
153
154 bool RecordInfo::IsStackAllocated() {
155   if (is_stack_allocated_ == kNotComputed) {
156     is_stack_allocated_ = kFalse;
157     for (Bases::iterator it = GetBases().begin();
158          it != GetBases().end();
159          ++it) {
160       if (it->second.info()->IsStackAllocated()) {
161         is_stack_allocated_ = kTrue;
162         return is_stack_allocated_;
163       }
164     }
165     for (CXXRecordDecl::method_iterator it = record_->method_begin();
166          it != record_->method_end();
167          ++it) {
168       if (it->getNameAsString() == kNewOperatorName &&
169           it->isDeleted() &&
170           Config::IsStackAnnotated(*it)) {
171         is_stack_allocated_ = kTrue;
172         return is_stack_allocated_;
173       }
174     }
175   }
176   return is_stack_allocated_;
177 }
178
179 bool RecordInfo::IsNonNewable() {
180   if (is_non_newable_ == kNotComputed) {
181     bool deleted = false;
182     bool all_deleted = true;
183     for (CXXRecordDecl::method_iterator it = record_->method_begin();
184          it != record_->method_end();
185          ++it) {
186       if (it->getNameAsString() == kNewOperatorName) {
187         deleted = it->isDeleted();
188         all_deleted = all_deleted && deleted;
189       }
190     }
191     is_non_newable_ = (deleted && all_deleted) ? kTrue : kFalse;
192   }
193   return is_non_newable_;
194 }
195
196 bool RecordInfo::IsOnlyPlacementNewable() {
197   if (is_only_placement_newable_ == kNotComputed) {
198     bool placement = false;
199     bool new_deleted = false;
200     for (CXXRecordDecl::method_iterator it = record_->method_begin();
201          it != record_->method_end();
202          ++it) {
203       if (it->getNameAsString() == kNewOperatorName) {
204         if (it->getNumParams() == 1) {
205           new_deleted = it->isDeleted();
206         } else if (it->getNumParams() == 2) {
207           placement = !it->isDeleted();
208         }
209       }
210     }
211     is_only_placement_newable_ = (placement && new_deleted) ? kTrue : kFalse;
212   }
213   return is_only_placement_newable_;
214 }
215
216 CXXMethodDecl* RecordInfo::DeclaresNewOperator() {
217   for (CXXRecordDecl::method_iterator it = record_->method_begin();
218        it != record_->method_end();
219        ++it) {
220     if (it->getNameAsString() == kNewOperatorName && it->getNumParams() == 1)
221       return *it;
222   }
223   return 0;
224 }
225
226 // An object requires a tracing method if it has any fields that need tracing
227 // or if it inherits from multiple bases that need tracing.
228 bool RecordInfo::RequiresTraceMethod() {
229   if (IsStackAllocated())
230     return false;
231   unsigned bases_with_trace = 0;
232   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
233     if (it->second.NeedsTracing().IsNeeded())
234       ++bases_with_trace;
235   }
236   if (bases_with_trace > 1)
237     return true;
238   GetFields();
239   return fields_need_tracing_.IsNeeded();
240 }
241
242 // Get the actual tracing method (ie, can be traceAfterDispatch if there is a
243 // dispatch method).
244 CXXMethodDecl* RecordInfo::GetTraceMethod() {
245   DetermineTracingMethods();
246   return trace_method_;
247 }
248
249 // Get the static trace dispatch method.
250 CXXMethodDecl* RecordInfo::GetTraceDispatchMethod() {
251   DetermineTracingMethods();
252   return trace_dispatch_method_;
253 }
254
255 CXXMethodDecl* RecordInfo::GetFinalizeDispatchMethod() {
256   DetermineTracingMethods();
257   return finalize_dispatch_method_;
258 }
259
260 RecordInfo::Bases& RecordInfo::GetBases() {
261   if (!bases_)
262     bases_ = CollectBases();
263   return *bases_;
264 }
265
266 bool RecordInfo::InheritsTrace() {
267   if (GetTraceMethod())
268     return true;
269   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
270     if (it->second.info()->InheritsTrace())
271       return true;
272   }
273   return false;
274 }
275
276 CXXMethodDecl* RecordInfo::InheritsNonVirtualTrace() {
277   if (CXXMethodDecl* trace = GetTraceMethod())
278     return trace->isVirtual() ? 0 : trace;
279   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
280     if (CXXMethodDecl* trace = it->second.info()->InheritsNonVirtualTrace())
281       return trace;
282   }
283   return 0;
284 }
285
286 // A (non-virtual) class is considered abstract in Blink if it has
287 // no public constructors and no create methods.
288 bool RecordInfo::IsConsideredAbstract() {
289   for (CXXRecordDecl::ctor_iterator it = record_->ctor_begin();
290        it != record_->ctor_end();
291        ++it) {
292     if (!it->isCopyOrMoveConstructor() && it->getAccess() == AS_public)
293       return false;
294   }
295   for (CXXRecordDecl::method_iterator it = record_->method_begin();
296        it != record_->method_end();
297        ++it) {
298     if (it->getNameAsString() == kCreateName)
299       return false;
300   }
301   return true;
302 }
303
304 RecordInfo::Bases* RecordInfo::CollectBases() {
305   // Compute the collection locally to avoid inconsistent states.
306   Bases* bases = new Bases;
307   if (!record_->hasDefinition())
308     return bases;
309   for (CXXRecordDecl::base_class_iterator it = record_->bases_begin();
310        it != record_->bases_end();
311        ++it) {
312     const CXXBaseSpecifier& spec = *it;
313     RecordInfo* info = cache_->Lookup(spec.getType());
314     if (!info)
315       continue;
316     CXXRecordDecl* base = info->record();
317     TracingStatus status = info->InheritsTrace()
318                                ? TracingStatus::Needed()
319                                : TracingStatus::Unneeded();
320     bases->insert(std::make_pair(base, BasePoint(spec, info, status)));
321   }
322   return bases;
323 }
324
325 RecordInfo::Fields& RecordInfo::GetFields() {
326   if (!fields_)
327     fields_ = CollectFields();
328   return *fields_;
329 }
330
331 RecordInfo::Fields* RecordInfo::CollectFields() {
332   // Compute the collection locally to avoid inconsistent states.
333   Fields* fields = new Fields;
334   if (!record_->hasDefinition())
335     return fields;
336   TracingStatus fields_status = TracingStatus::Unneeded();
337   for (RecordDecl::field_iterator it = record_->field_begin();
338        it != record_->field_end();
339        ++it) {
340     FieldDecl* field = *it;
341     // Ignore fields annotated with the GC_PLUGIN_IGNORE macro.
342     if (Config::IsIgnoreAnnotated(field))
343       continue;
344     if (Edge* edge = CreateEdge(field->getType().getTypePtrOrNull())) {
345       fields_status = fields_status.LUB(edge->NeedsTracing(Edge::kRecursive));
346       fields->insert(std::make_pair(field, FieldPoint(field, edge)));
347     }
348   }
349   fields_need_tracing_ = fields_status;
350   return fields;
351 }
352
353 void RecordInfo::DetermineTracingMethods() {
354   if (determined_trace_methods_)
355     return;
356   determined_trace_methods_ = true;
357   if (Config::IsGCBase(name_))
358     return;
359   CXXMethodDecl* trace = 0;
360   CXXMethodDecl* traceAfterDispatch = 0;
361   bool isTraceAfterDispatch;
362   for (CXXRecordDecl::method_iterator it = record_->method_begin();
363        it != record_->method_end();
364        ++it) {
365     if (Config::IsTraceMethod(*it, &isTraceAfterDispatch)) {
366       if (isTraceAfterDispatch) {
367         traceAfterDispatch = *it;
368       } else {
369         trace = *it;
370       }
371     } else if (it->getNameAsString() == kFinalizeName) {
372       finalize_dispatch_method_ = *it;
373     }
374   }
375   if (traceAfterDispatch) {
376     trace_method_ = traceAfterDispatch;
377     trace_dispatch_method_ = trace;
378   } else {
379     // TODO: Can we never have a dispatch method called trace without the same
380     // class defining a traceAfterDispatch method?
381     trace_method_ = trace;
382     trace_dispatch_method_ = 0;
383   }
384   if (trace_dispatch_method_ && finalize_dispatch_method_)
385     return;
386   // If this class does not define dispatching methods inherit them.
387   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
388     // TODO: Does it make sense to inherit multiple dispatch methods?
389     if (CXXMethodDecl* dispatch = it->second.info()->GetTraceDispatchMethod()) {
390       assert(!trace_dispatch_method_ && "Multiple trace dispatching methods");
391       trace_dispatch_method_ = dispatch;
392     }
393     if (CXXMethodDecl* dispatch =
394             it->second.info()->GetFinalizeDispatchMethod()) {
395       assert(!finalize_dispatch_method_ &&
396              "Multiple finalize dispatching methods");
397       finalize_dispatch_method_ = dispatch;
398     }
399   }
400 }
401
402 // TODO: Add classes with a finalize() method that specialize FinalizerTrait.
403 bool RecordInfo::NeedsFinalization() {
404   if (does_need_finalization_ == kNotComputed) {
405     // Rely on hasNonTrivialDestructor(), but if the only
406     // identifiable reason for it being true is the presence
407     // of a safely ignorable class as a direct base,
408     // or we're processing such an 'ignorable' class, then it does
409     // not need finalization.
410     does_need_finalization_ =
411         record_->hasNonTrivialDestructor() ? kTrue : kFalse;
412     if (!does_need_finalization_)
413       return does_need_finalization_;
414
415     // Processing a class with a safely-ignorable destructor.
416     NamespaceDecl* ns =
417         dyn_cast<NamespaceDecl>(record_->getDeclContext());
418     if (ns && Config::HasIgnorableDestructor(ns->getName(), name_)) {
419       does_need_finalization_ = kFalse;
420       return does_need_finalization_;
421     }
422
423     CXXDestructorDecl* dtor = record_->getDestructor();
424     if (dtor && dtor->isUserProvided())
425       return does_need_finalization_;
426     for (Fields::iterator it = GetFields().begin();
427          it != GetFields().end();
428          ++it) {
429       if (it->second.edge()->NeedsFinalization())
430         return does_need_finalization_;
431     }
432
433     for (Bases::iterator it = GetBases().begin();
434          it != GetBases().end();
435          ++it) {
436       if (it->second.info()->NeedsFinalization())
437         return does_need_finalization_;
438     }
439     // Destructor was non-trivial due to bases with destructors that
440     // can be safely ignored. Hence, no need for finalization.
441     does_need_finalization_ = kFalse;
442   }
443   return does_need_finalization_;
444 }
445
446 // A class needs tracing if:
447 // - it is allocated on the managed heap,
448 // - it is derived from a class that needs tracing, or
449 // - it contains fields that need tracing.
450 // TODO: Defining NeedsTracing based on whether a class defines a trace method
451 // (of the proper signature) over approximates too much. The use of transition
452 // types causes some classes to have trace methods without them needing to be
453 // traced.
454 TracingStatus RecordInfo::NeedsTracing(Edge::NeedsTracingOption option) {
455   if (IsGCAllocated())
456     return TracingStatus::Needed();
457
458   if (IsStackAllocated())
459     return TracingStatus::Unneeded();
460
461   for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) {
462     if (it->second.info()->NeedsTracing(option).IsNeeded())
463       return TracingStatus::Needed();
464   }
465
466   if (option == Edge::kRecursive)
467     GetFields();
468
469   return fields_need_tracing_;
470 }
471
472 Edge* RecordInfo::CreateEdge(const Type* type) {
473   if (!type) {
474     return 0;
475   }
476
477   if (type->isPointerType()) {
478     if (Edge* ptr = CreateEdge(type->getPointeeType().getTypePtrOrNull()))
479       return new RawPtr(ptr, false);
480     return 0;
481   }
482
483   RecordInfo* info = cache_->Lookup(type);
484
485   // If the type is neither a pointer or a C++ record we ignore it.
486   if (!info) {
487     return 0;
488   }
489
490   TemplateArgs args;
491
492   if (Config::IsRawPtr(info->name()) && info->GetTemplateArgs(1, &args)) {
493     if (Edge* ptr = CreateEdge(args[0]))
494       return new RawPtr(ptr, true);
495     return 0;
496   }
497
498   if (Config::IsRefPtr(info->name()) && info->GetTemplateArgs(1, &args)) {
499     if (Edge* ptr = CreateEdge(args[0]))
500       return new RefPtr(ptr);
501     return 0;
502   }
503
504   if (Config::IsOwnPtr(info->name()) && info->GetTemplateArgs(1, &args)) {
505     if (Edge* ptr = CreateEdge(args[0]))
506       return new OwnPtr(ptr);
507     return 0;
508   }
509
510   if (Config::IsMember(info->name()) && info->GetTemplateArgs(1, &args)) {
511     if (Edge* ptr = CreateEdge(args[0]))
512       return new Member(ptr);
513     return 0;
514   }
515
516   if (Config::IsWeakMember(info->name()) && info->GetTemplateArgs(1, &args)) {
517     if (Edge* ptr = CreateEdge(args[0]))
518       return new WeakMember(ptr);
519     return 0;
520   }
521
522   if (Config::IsPersistent(info->name())) {
523     // Persistent might refer to v8::Persistent, so check the name space.
524     // TODO: Consider using a more canonical identification than names.
525     NamespaceDecl* ns =
526         dyn_cast<NamespaceDecl>(info->record()->getDeclContext());
527     if (!ns || ns->getName() != "blink")
528       return 0;
529     if (!info->GetTemplateArgs(1, &args))
530       return 0;
531     if (Edge* ptr = CreateEdge(args[0]))
532       return new Persistent(ptr);
533     return 0;
534   }
535
536   if (Config::IsGCCollection(info->name()) ||
537       Config::IsWTFCollection(info->name())) {
538     bool is_root = Config::IsPersistentGCCollection(info->name());
539     bool on_heap = is_root || info->IsHeapAllocatedCollection();
540     size_t count = Config::CollectionDimension(info->name());
541     if (!info->GetTemplateArgs(count, &args))
542       return 0;
543     Collection* edge = new Collection(info, on_heap, is_root);
544     for (TemplateArgs::iterator it = args.begin(); it != args.end(); ++it) {
545       if (Edge* member = CreateEdge(*it)) {
546         edge->members().push_back(member);
547       }
548       // TODO: Handle the case where we fail to create an edge (eg, if the
549       // argument is a primitive type or just not fully known yet).
550     }
551     return edge;
552   }
553
554   return new Value(info);
555 }