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 |