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