| 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 307 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 318 private: | 318 private: |
| 319 RecordInfo* receiver_; | 319 RecordInfo* receiver_; |
| 320 bool dispatched_to_receiver_; | 320 bool dispatched_to_receiver_; |
| 321 }; | 321 }; |
| 322 | 322 |
| 323 // This visitor checks a tracing method by traversing its body. | 323 // This visitor checks a tracing method by traversing its body. |
| 324 // - A member field is considered traced if it is referenced in the body. | 324 // - A member field is considered traced if it is referenced in the body. |
| 325 // - A base is traced if a base-qualified call to a trace method is found. | 325 // - A base is traced if a base-qualified call to a trace method is found. |
| 326 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { | 326 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
| 327 public: | 327 public: |
| 328 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) | 328 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info, RecordCache* cache) |
| 329 : trace_(trace), info_(info), delegates_to_traceimpl_(false) {} | 329 : trace_(trace), |
| 330 info_(info), |
| 331 cache_(cache), |
| 332 delegates_to_traceimpl_(false) { |
| 333 } |
| 330 | 334 |
| 331 bool delegates_to_traceimpl() const { return delegates_to_traceimpl_; } | 335 bool delegates_to_traceimpl() const { return delegates_to_traceimpl_; } |
| 332 | 336 |
| 333 bool VisitMemberExpr(MemberExpr* member) { | 337 bool VisitMemberExpr(MemberExpr* member) { |
| 334 // In weak callbacks, consider any occurrence as a correct usage. | 338 // In weak callbacks, consider any occurrence as a correct usage. |
| 335 // TODO: We really want to require that isAlive is checked on manually | 339 // TODO: We really want to require that isAlive is checked on manually |
| 336 // processed weak fields. | 340 // processed weak fields. |
| 337 if (IsWeakCallback()) { | 341 if (IsWeakCallback()) { |
| 338 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) | 342 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) |
| 339 FoundField(field); | 343 FoundField(field); |
| (...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 callee_record = trace_decl->getParent(); | 538 callee_record = trace_decl->getParent(); |
| 535 func_name = callee->getMemberName().getAsString(); | 539 func_name = callee->getMemberName().getAsString(); |
| 536 } | 540 } |
| 537 | 541 |
| 538 if (!callee_record) | 542 if (!callee_record) |
| 539 return false; | 543 return false; |
| 540 | 544 |
| 541 if (!IsTraceCallName(func_name)) | 545 if (!IsTraceCallName(func_name)) |
| 542 return false; | 546 return false; |
| 543 | 547 |
| 544 RecordInfo::Bases::iterator iter = info_->GetBases().find(callee_record); | 548 for (auto& base : info_->GetBases()) { |
| 545 if (iter == info_->GetBases().end()) | 549 // We want to deal with omitted trace() function in an intermediary |
| 546 return false; | 550 // class in the class hierarchy, e.g.: |
| 551 // class A : public GarbageCollected<A> { trace() { ... } }; |
| 552 // class B : public A { /* No trace(); have nothing to trace. */ }; |
| 553 // class C : public B { trace() { B::trace(visitor); } } |
| 554 // where, B::trace() is actually A::trace(), and in some cases we get |
| 555 // A as |callee_record| instead of B. We somehow need to mark B as |
| 556 // traced if we find A::trace() call. |
| 557 // |
| 558 // To solve this, here we keep going up the class hierarchy as long as |
| 559 // they are not required to have a trace method. The implementation is |
| 560 // a simple DFS, where |base_records| represents the set of base classes |
| 561 // we need to visit. |
| 547 | 562 |
| 548 iter->second.MarkTraced(); | 563 std::vector<CXXRecordDecl*> base_records; |
| 549 return true; | 564 base_records.push_back(base.first); |
| 565 |
| 566 while (!base_records.empty()) { |
| 567 CXXRecordDecl* base_record = base_records.back(); |
| 568 base_records.pop_back(); |
| 569 |
| 570 if (base_record == callee_record) { |
| 571 // If we find a matching trace method, pretend the user has written |
| 572 // a correct trace() method of the base; in the example above, we |
| 573 // find A::trace() here and mark B as correctly traced. |
| 574 base.second.MarkTraced(); |
| 575 return true; |
| 576 } |
| 577 |
| 578 if (RecordInfo* base_info = cache_->Lookup(base_record)) { |
| 579 if (!base_info->RequiresTraceMethod()) { |
| 580 // If this base class is not required to have a trace method, then |
| 581 // the actual trace method may be defined in an ancestor. |
| 582 for (auto& inner_base : base_info->GetBases()) |
| 583 base_records.push_back(inner_base.first); |
| 584 } |
| 585 } |
| 586 } |
| 587 } |
| 588 |
| 589 return false; |
| 550 } | 590 } |
| 551 | 591 |
| 552 bool CheckTraceFieldCall(CXXMemberCallExpr* call) { | 592 bool CheckTraceFieldCall(CXXMemberCallExpr* call) { |
| 553 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), | 593 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), |
| 554 call->getRecordDecl(), | 594 call->getRecordDecl(), |
| 555 call->getArg(0)); | 595 call->getArg(0)); |
| 556 } | 596 } |
| 557 | 597 |
| 558 bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { | 598 bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { |
| 559 if (name != kTraceName || !Config::IsVisitor(callee->getName())) | 599 if (name != kTraceName || !Config::IsVisitor(callee->getName())) |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 602 return false; | 642 return false; |
| 603 } | 643 } |
| 604 return true; | 644 return true; |
| 605 } | 645 } |
| 606 private: | 646 private: |
| 607 MemberExpr* member_; | 647 MemberExpr* member_; |
| 608 FieldDecl* field_; | 648 FieldDecl* field_; |
| 609 }; | 649 }; |
| 610 | 650 |
| 611 // Nested checking for weak callbacks. | 651 // Nested checking for weak callbacks. |
| 612 CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} | 652 CheckTraceVisitor(RecordInfo* info) |
| 653 : trace_(nullptr), info_(info), cache_(nullptr) {} |
| 613 | 654 |
| 614 bool IsWeakCallback() { return !trace_; } | 655 bool IsWeakCallback() { return !trace_; } |
| 615 | 656 |
| 616 void MarkTraced(RecordInfo::Fields::iterator it) { | 657 void MarkTraced(RecordInfo::Fields::iterator it) { |
| 617 // In a weak callback we can't mark strong fields as traced. | 658 // In a weak callback we can't mark strong fields as traced. |
| 618 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) | 659 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) |
| 619 return; | 660 return; |
| 620 it->second.MarkTraced(); | 661 it->second.MarkTraced(); |
| 621 } | 662 } |
| 622 | 663 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 636 } | 677 } |
| 637 } else { | 678 } else { |
| 638 RecordInfo::Fields::iterator it = info_->GetFields().find(field); | 679 RecordInfo::Fields::iterator it = info_->GetFields().find(field); |
| 639 if (it != info_->GetFields().end()) | 680 if (it != info_->GetFields().end()) |
| 640 MarkTraced(it); | 681 MarkTraced(it); |
| 641 } | 682 } |
| 642 } | 683 } |
| 643 | 684 |
| 644 CXXMethodDecl* trace_; | 685 CXXMethodDecl* trace_; |
| 645 RecordInfo* info_; | 686 RecordInfo* info_; |
| 687 RecordCache* cache_; |
| 646 bool delegates_to_traceimpl_; | 688 bool delegates_to_traceimpl_; |
| 647 }; | 689 }; |
| 648 | 690 |
| 649 // This visitor checks that the fields of a class and the fields of | 691 // This visitor checks that the fields of a class and the fields of |
| 650 // its part objects don't define GC roots. | 692 // its part objects don't define GC roots. |
| 651 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { | 693 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { |
| 652 public: | 694 public: |
| 653 typedef std::vector<FieldPoint*> RootPath; | 695 typedef std::vector<FieldPoint*> RootPath; |
| 654 typedef std::set<RecordInfo*> VisitingSet; | 696 typedef std::set<RecordInfo*> VisitingSet; |
| 655 typedef std::vector<RootPath> Errors; | 697 typedef std::vector<RootPath> Errors; |
| (...skipping 724 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1380 if (trace_type == Config::TRACE_METHOD) { | 1422 if (trace_type == Config::TRACE_METHOD) { |
| 1381 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1423 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
| 1382 it != parent->GetBases().end(); | 1424 it != parent->GetBases().end(); |
| 1383 ++it) { | 1425 ++it) { |
| 1384 RecordInfo* base = it->second.info(); | 1426 RecordInfo* base = it->second.info(); |
| 1385 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) | 1427 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
| 1386 ReportOverriddenNonVirtualTrace(parent, trace, other); | 1428 ReportOverriddenNonVirtualTrace(parent, trace, other); |
| 1387 } | 1429 } |
| 1388 } | 1430 } |
| 1389 | 1431 |
| 1390 CheckTraceVisitor visitor(trace, parent); | 1432 CheckTraceVisitor visitor(trace, parent, &cache_); |
| 1391 visitor.TraverseCXXMethodDecl(trace); | 1433 visitor.TraverseCXXMethodDecl(trace); |
| 1392 | 1434 |
| 1393 // Skip reporting if this trace method is a just delegate to | 1435 // Skip reporting if this trace method is a just delegate to |
| 1394 // traceImpl (or traceAfterDispatchImpl) method. We will report on | 1436 // traceImpl (or traceAfterDispatchImpl) method. We will report on |
| 1395 // CheckTraceMethod on traceImpl method. | 1437 // CheckTraceMethod on traceImpl method. |
| 1396 if (visitor.delegates_to_traceimpl()) | 1438 if (visitor.delegates_to_traceimpl()) |
| 1397 return; | 1439 return; |
| 1398 | 1440 |
| 1399 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1441 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
| 1400 it != parent->GetBases().end(); | 1442 it != parent->GetBases().end(); |
| (...skipping 589 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1990 | 2032 |
| 1991 private: | 2033 private: |
| 1992 BlinkGCPluginOptions options_; | 2034 BlinkGCPluginOptions options_; |
| 1993 }; | 2035 }; |
| 1994 | 2036 |
| 1995 } // namespace | 2037 } // namespace |
| 1996 | 2038 |
| 1997 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 2039 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( |
| 1998 "blink-gc-plugin", | 2040 "blink-gc-plugin", |
| 1999 "Check Blink GC invariants"); | 2041 "Check Blink GC invariants"); |
| OLD | NEW |