Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // This clang plugin checks various invariants of the Blink garbage | 5 // This clang plugin checks various invariants of the Blink garbage |
| 6 // collection infrastructure. | 6 // collection infrastructure. |
| 7 // | 7 // |
| 8 // Errors are described at: | 8 // Errors are described at: |
| 9 // http://www.chromium.org/developers/blink-gc-plugin-errors | 9 // http://www.chromium.org/developers/blink-gc-plugin-errors |
| 10 | 10 |
| (...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 300 }; | 300 }; |
| 301 | 301 |
| 302 // This visitor checks a tracing method by traversing its body. | 302 // This visitor checks a tracing method by traversing its body. |
| 303 // - A member field is considered traced if it is referenced in the body. | 303 // - A member field is considered traced if it is referenced in the body. |
| 304 // - A base is traced if a base-qualified call to a trace method is found. | 304 // - A base is traced if a base-qualified call to a trace method is found. |
| 305 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { | 305 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
| 306 public: | 306 public: |
| 307 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) | 307 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) |
| 308 : trace_(trace), info_(info) {} | 308 : trace_(trace), info_(info) {} |
| 309 | 309 |
| 310 // Allow recursive traversal by using VisitMemberExpr. | |
| 311 bool VisitMemberExpr(MemberExpr* member) { | 310 bool VisitMemberExpr(MemberExpr* member) { |
| 312 // If this member expression references a field decl, mark it as traced. | 311 // In weak callbacks, consider any occurrence as a correct usage. |
| 313 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { | 312 // TODO: We really want to require that isAlive is checked on manually |
| 314 if (IsTemplateInstantiation(info_->record())) { | 313 // processed weak fields. |
| 315 // Pointer equality on fields does not work for template instantiations. | 314 if (IsWeakCallback()) { |
| 316 // The trace method refers to fields of the template definition which | 315 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) |
| 317 // are different from the instantiated fields that need to be traced. | 316 FoundField(field); |
| 318 const string& name = field->getNameAsString(); | 317 } |
| 318 return true; | |
| 319 } | |
| 320 | |
| 321 bool VisitCallExpr(CallExpr* call) { | |
| 322 // In weak callbacks we don't check calls (see VisitMemberExpr). | |
| 323 if (IsWeakCallback()) | |
| 324 return true; | |
| 325 | |
| 326 // A tracing call will have either a |visitor| or a |m_field| argument. | |
| 327 // A registerWeakMembers call will have a |this| argument. | |
| 328 if (call->getNumArgs() != 1) | |
| 329 return true; | |
| 330 | |
| 331 Expr* callee = call->getCallee(); | |
| 332 Expr* arg = call->getArg(0); | |
| 333 | |
| 334 if (CXXDependentScopeMemberExpr* expr = | |
| 335 dyn_cast<CXXDependentScopeMemberExpr>(callee)) { | |
| 336 CheckCXXDependentScopeMemberExpr(expr); | |
| 337 return true; | |
| 338 } | |
| 339 | |
| 340 if (UnresolvedMemberExpr* expr = dyn_cast<UnresolvedMemberExpr>(callee)) { | |
| 341 // If we find a call to registerWeakMembers which is unresolved we | |
| 342 // unsoundly consider all weak members as traced. | |
| 343 // TODO: Find out how to validate weak member tracing for unresolved call. | |
| 344 if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) { | |
| 319 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); | 345 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); |
| 320 it != info_->GetFields().end(); | 346 it != info_->GetFields().end(); |
| 321 ++it) { | 347 ++it) { |
| 322 if (it->first->getNameAsString() == name) { | 348 if (it->second.edge()->IsWeakMember()) |
| 323 MarkTraced(it); | 349 it->second.MarkTraced(); |
| 324 break; | |
| 325 } | |
| 326 } | 350 } |
| 327 } else { | |
| 328 RecordInfo::Fields::iterator it = info_->GetFields().find(field); | |
| 329 if (it != info_->GetFields().end()) | |
| 330 MarkTraced(it); | |
| 331 } | 351 } |
| 352 | |
| 353 QualType base = expr->getBaseType(); | |
| 354 if (!base->isPointerType()) | |
| 355 return true; | |
| 356 CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); | |
| 357 if (decl) | |
| 358 CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); | |
| 332 return true; | 359 return true; |
| 333 } | 360 } |
| 334 | 361 |
| 335 // If this is a weak callback function we only check field tracing. | 362 if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { |
| 363 if (CheckTraceFieldCall(expr) || CheckRegisterWeakMembers(expr)) | |
| 364 return true; | |
| 365 } | |
| 366 | |
| 367 CheckTraceBaseCall(call); | |
| 368 return true; | |
| 369 } | |
| 370 | |
| 371 private: | |
| 372 | |
| 373 // Trace calls from a templated derived class to its templated super class | |
| 374 // result in a DependentScopeMemberExpr because the concrete class depends on | |
| 375 // the instantiation of any shared template parameters. In this case we | |
| 376 // compare the type names. | |
| 377 void CheckCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr* member) { | |
| 336 if (IsWeakCallback()) | 378 if (IsWeakCallback()) |
| 337 return true; | 379 return; |
| 338 | 380 |
| 339 // For method calls, check tracing of bases and other special GC methods. | 381 // It is unknown if the member resolves to a method so we string compare. |
|
haraken
2014/07/31 15:37:11
we string compare => we compare strings
| |
| 340 if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) { | 382 if (member->getMember().getAsString() != trace_->getName()) |
| 341 const string& name = fn->getNameAsString(); | 383 return; |
| 342 // Check weak callbacks. | |
| 343 if (name == kRegisterWeakMembersName) { | |
| 344 if (fn->isTemplateInstantiation()) { | |
| 345 const TemplateArgumentList& args = | |
| 346 *fn->getTemplateSpecializationInfo()->TemplateArguments; | |
| 347 // The second template argument is the callback method. | |
| 348 if (args.size() > 1 && | |
| 349 args[1].getKind() == TemplateArgument::Declaration) { | |
| 350 if (FunctionDecl* callback = | |
| 351 dyn_cast<FunctionDecl>(args[1].getAsDecl())) { | |
| 352 if (callback->hasBody()) { | |
| 353 CheckTraceVisitor nested_visitor(info_); | |
| 354 nested_visitor.TraverseStmt(callback->getBody()); | |
| 355 } | |
| 356 } | |
| 357 } | |
| 358 } | |
| 359 return true; | |
| 360 } | |
| 361 | 384 |
| 362 // Currently, a manually dispatched class cannot have mixin bases (having | 385 NestedNameSpecifier* qual = member->getQualifier(); |
| 363 // one would add a vtable which we explicitly check against). This means | 386 if (!qual) |
| 364 // that we can only make calls to a trace method of the same name. Revisit | 387 return; |
| 365 // this if our mixin/vtable assumption changes. | 388 |
| 366 if (Config::IsTraceMethod(fn) && | 389 const Type* type = qual->getAsType(); |
| 367 fn->getName() == trace_->getName() && | 390 if (!type) |
| 368 member->hasQualifier()) { | 391 return; |
| 369 if (const Type* type = member->getQualifier()->getAsType()) { | 392 |
| 370 if (CXXRecordDecl* decl = type->getAsCXXRecordDecl()) { | 393 const TemplateSpecializationType* tmpl_type = |
| 371 RecordInfo::Bases::iterator it = info_->GetBases().find(decl); | 394 type->getAs<TemplateSpecializationType>(); |
| 372 if (it != info_->GetBases().end()) | 395 if (!tmpl_type) |
| 373 it->second.MarkTraced(); | 396 return; |
| 397 | |
| 398 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); | |
| 399 if (!tmpl_decl) | |
| 400 return; | |
| 401 | |
| 402 CXXRecordDecl* rec = dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); | |
| 403 if (!rec) | |
| 404 return; | |
| 405 | |
| 406 RecordInfo::Bases::iterator it; | |
| 407 for (it = info_->GetBases().begin(); it != info_->GetBases().end(); ++it) { | |
| 408 if (it->first->getName() == rec->getName()) | |
| 409 it->second.MarkTraced(); | |
| 410 } | |
| 411 } | |
| 412 | |
| 413 bool CheckTraceBaseCall(CallExpr* call) { | |
| 414 MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee()); | |
| 415 if (!callee) | |
| 416 return false; | |
| 417 | |
| 418 FunctionDecl* fn = dyn_cast<FunctionDecl>(callee->getMemberDecl()); | |
| 419 if (!fn || !Config::IsTraceMethod(fn)) | |
| 420 return false; | |
| 421 | |
| 422 // Currently, a manually dispatched class cannot have mixin bases (having | |
| 423 // one would add a vtable which we explicitly check against). This means | |
| 424 // that we can only make calls to a trace method of the same name. Revisit | |
| 425 // this if our mixin/vtable assumption changes. | |
| 426 if (fn->getName() != trace_->getName()) | |
| 427 return false; | |
| 428 | |
| 429 CXXRecordDecl* decl = 0; | |
| 430 if (callee && callee->hasQualifier()) { | |
| 431 if (const Type* type = callee->getQualifier()->getAsType()) | |
| 432 decl = type->getAsCXXRecordDecl(); | |
| 433 } | |
| 434 if (!decl) | |
| 435 return false; | |
| 436 | |
| 437 RecordInfo::Bases::iterator it = info_->GetBases().find(decl); | |
| 438 if (it != info_->GetBases().end()) { | |
| 439 it->second.MarkTraced(); | |
| 440 } | |
| 441 | |
| 442 return true; | |
| 443 } | |
| 444 | |
| 445 bool CheckTraceFieldCall(CXXMemberCallExpr* call) { | |
| 446 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), | |
| 447 call->getRecordDecl(), | |
| 448 call->getArg(0)); | |
| 449 } | |
| 450 | |
| 451 bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { | |
| 452 if (name != kTraceName || !Config::IsVisitor(callee->getName())) | |
| 453 return false; | |
| 454 | |
| 455 FindFieldVisitor finder; | |
| 456 finder.TraverseStmt(arg); | |
| 457 if (finder.field()) | |
| 458 FoundField(finder.field()); | |
| 459 | |
| 460 return true; | |
| 461 } | |
| 462 | |
| 463 bool CheckRegisterWeakMembers(CXXMemberCallExpr* call) { | |
| 464 CXXMethodDecl* fn = call->getMethodDecl(); | |
| 465 if (fn->getName() != kRegisterWeakMembersName) | |
| 466 return false; | |
| 467 | |
| 468 if (fn->isTemplateInstantiation()) { | |
| 469 const TemplateArgumentList& args = | |
| 470 *fn->getTemplateSpecializationInfo()->TemplateArguments; | |
| 471 // The second template argument is the callback method. | |
| 472 if (args.size() > 1 && | |
| 473 args[1].getKind() == TemplateArgument::Declaration) { | |
| 474 if (FunctionDecl* callback = | |
| 475 dyn_cast<FunctionDecl>(args[1].getAsDecl())) { | |
| 476 if (callback->hasBody()) { | |
| 477 CheckTraceVisitor nested_visitor(info_); | |
| 478 nested_visitor.TraverseStmt(callback->getBody()); | |
| 374 } | 479 } |
| 375 } | 480 } |
| 376 } | 481 } |
| 377 } | 482 } |
| 378 return true; | 483 return true; |
| 379 } | 484 } |
| 380 | 485 |
| 381 private: | 486 class FindFieldVisitor : public RecursiveASTVisitor<FindFieldVisitor> { |
| 487 public: | |
| 488 FindFieldVisitor() : member_(0), field_(0) {} | |
| 489 MemberExpr* member() const { return member_; } | |
| 490 FieldDecl* field() const { return field_; } | |
| 491 bool TraverseMemberExpr(MemberExpr* member) { | |
| 492 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { | |
| 493 member_ = member; | |
| 494 field_ = field; | |
| 495 return false; | |
| 496 } | |
| 497 return true; | |
| 498 } | |
| 499 private: | |
| 500 MemberExpr* member_; | |
| 501 FieldDecl* field_; | |
| 502 }; | |
| 503 | |
| 382 // Nested checking for weak callbacks. | 504 // Nested checking for weak callbacks. |
| 383 CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} | 505 CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} |
| 384 | 506 |
| 385 bool IsWeakCallback() { return !trace_; } | 507 bool IsWeakCallback() { return !trace_; } |
| 386 | 508 |
| 387 void MarkTraced(RecordInfo::Fields::iterator it) { | 509 void MarkTraced(RecordInfo::Fields::iterator it) { |
| 388 // In a weak callback we can't mark strong fields as traced. | 510 // In a weak callback we can't mark strong fields as traced. |
| 389 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) | 511 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) |
| 390 return; | 512 return; |
| 391 it->second.MarkTraced(); | 513 it->second.MarkTraced(); |
| 392 } | 514 } |
| 393 | 515 |
| 516 void FoundField(FieldDecl* field) { | |
| 517 if (IsTemplateInstantiation(info_->record())) { | |
| 518 // Pointer equality on fields does not work for template instantiations. | |
| 519 // The trace method refers to fields of the template definition which | |
| 520 // are different from the instantiated fields that need to be traced. | |
| 521 const string& name = field->getNameAsString(); | |
| 522 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); | |
| 523 it != info_->GetFields().end(); | |
| 524 ++it) { | |
| 525 if (it->first->getNameAsString() == name) { | |
| 526 MarkTraced(it); | |
| 527 break; | |
| 528 } | |
| 529 } | |
| 530 } else { | |
| 531 RecordInfo::Fields::iterator it = info_->GetFields().find(field); | |
| 532 if (it != info_->GetFields().end()) | |
| 533 MarkTraced(it); | |
| 534 } | |
| 535 } | |
| 536 | |
| 394 CXXMethodDecl* trace_; | 537 CXXMethodDecl* trace_; |
| 395 RecordInfo* info_; | 538 RecordInfo* info_; |
| 396 }; | 539 }; |
| 397 | 540 |
| 398 // This visitor checks that the fields of a class and the fields of | 541 // This visitor checks that the fields of a class and the fields of |
| 399 // its part objects don't define GC roots. | 542 // its part objects don't define GC roots. |
| 400 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { | 543 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { |
| 401 public: | 544 public: |
| 402 typedef std::vector<FieldPoint*> RootPath; | 545 typedef std::vector<FieldPoint*> RootPath; |
| 403 typedef std::vector<RootPath> Errors; | 546 typedef std::vector<RootPath> Errors; |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 567 // garbage collection infrastructure. | 710 // garbage collection infrastructure. |
| 568 class BlinkGCPluginConsumer : public ASTConsumer { | 711 class BlinkGCPluginConsumer : public ASTConsumer { |
| 569 public: | 712 public: |
| 570 BlinkGCPluginConsumer(CompilerInstance& instance, | 713 BlinkGCPluginConsumer(CompilerInstance& instance, |
| 571 const BlinkGCPluginOptions& options) | 714 const BlinkGCPluginOptions& options) |
| 572 : instance_(instance), | 715 : instance_(instance), |
| 573 diagnostic_(instance.getDiagnostics()), | 716 diagnostic_(instance.getDiagnostics()), |
| 574 options_(options), | 717 options_(options), |
| 575 json_(0) { | 718 json_(0) { |
| 576 | 719 |
| 577 // Only check structures in the blink, blink and WebKit namespaces. | 720 // Only check structures in the blink and WebKit namespaces. |
| 578 options_.checked_namespaces.insert("blink"); | |
| 579 options_.checked_namespaces.insert("blink"); | 721 options_.checked_namespaces.insert("blink"); |
| 580 options_.checked_namespaces.insert("WebKit"); | 722 options_.checked_namespaces.insert("WebKit"); |
| 581 | 723 |
| 582 // Ignore GC implementation files. | 724 // Ignore GC implementation files. |
| 583 options_.ignored_directories.push_back("/heap/"); | 725 options_.ignored_directories.push_back("/heap/"); |
| 584 | 726 |
| 585 // Register warning/error messages. | 727 // Register warning/error messages. |
| 586 diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID( | 728 diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID( |
| 587 getErrorLevel(), kClassMustLeftMostlyDeriveGC); | 729 getErrorLevel(), kClassMustLeftMostlyDeriveGC); |
| 588 diag_class_requires_trace_method_ = | 730 diag_class_requires_trace_method_ = |
| (...skipping 888 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1477 | 1619 |
| 1478 private: | 1620 private: |
| 1479 BlinkGCPluginOptions options_; | 1621 BlinkGCPluginOptions options_; |
| 1480 }; | 1622 }; |
| 1481 | 1623 |
| 1482 } // namespace | 1624 } // namespace |
| 1483 | 1625 |
| 1484 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 1626 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( |
| 1485 "blink-gc-plugin", | 1627 "blink-gc-plugin", |
| 1486 "Check Blink GC invariants"); | 1628 "Check Blink GC invariants"); |
| OLD | NEW |