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 (RecordInfo::Bases::iterator iter = info_->GetBases().begin(); |
545 if (iter == info_->GetBases().end()) | 549 iter != info_->GetBases().end(); ++iter) { |
kouhei (in TOK)
2015/03/02 04:24:13
Nit: use foreach?
Yuta Kitamura
2015/03/02 07:36:49
Done.
| |
546 return false; | 550 // We want to deal with omitted trace() function in an intermediary |
551 // class in the class hierarchy, e.g.: | |
552 // class A : public GarbageCollected<A> { trace() { ... } }; | |
553 // class B : public A { /* No trace(); have nothing to trace. */ }; | |
554 // class C : public B { trace() { B::trace(visitor); } } | |
555 // where, B::trace() is actually A::trace(), and in some cases we get | |
556 // A as |callee_record| instead of B. We somehow need to mark B as | |
557 // traced if we find A::trace() call. | |
558 // | |
559 // To solve this, here we keep going up the class hierarchy as long as | |
560 // they are not required to have a trace method. The implementation is | |
561 // a simple DFS, where |base_records| represents the set of base classes | |
562 // we need to visit. | |
547 | 563 |
548 iter->second.MarkTraced(); | 564 std::vector<CXXRecordDecl*> base_records; |
549 return true; | 565 base_records.push_back(iter->first); |
566 | |
567 while (!base_records.empty()) { | |
568 CXXRecordDecl* base_record = base_records.back(); | |
569 base_records.pop_back(); | |
570 | |
571 if (base_record == callee_record) { | |
572 // If we find a matching trace method, pretend the user has written | |
573 // a correct trace() method of the base; in the example above, we | |
574 // find A::trace() here and mark B as correctly traced. | |
575 iter->second.MarkTraced(); | |
576 return true; | |
577 } | |
578 | |
579 if (RecordInfo* base_info = cache_->Lookup(base_record)) { | |
580 if (!base_info->RequiresTraceMethod()) { | |
581 // If this base class is not required to have a trace method, then | |
582 // the actual trace method may be defined in an ancestor. | |
583 for (RecordInfo::Bases::iterator inner_iter = | |
584 base_info->GetBases().begin(); | |
585 inner_iter != base_info->GetBases().end(); ++inner_iter) { | |
586 base_records.push_back(inner_iter->first); | |
587 } | |
588 } | |
589 } | |
590 } | |
591 } | |
592 | |
593 return false; | |
550 } | 594 } |
551 | 595 |
552 bool CheckTraceFieldCall(CXXMemberCallExpr* call) { | 596 bool CheckTraceFieldCall(CXXMemberCallExpr* call) { |
553 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), | 597 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), |
554 call->getRecordDecl(), | 598 call->getRecordDecl(), |
555 call->getArg(0)); | 599 call->getArg(0)); |
556 } | 600 } |
557 | 601 |
558 bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { | 602 bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { |
559 if (name != kTraceName || !Config::IsVisitor(callee->getName())) | 603 if (name != kTraceName || !Config::IsVisitor(callee->getName())) |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
602 return false; | 646 return false; |
603 } | 647 } |
604 return true; | 648 return true; |
605 } | 649 } |
606 private: | 650 private: |
607 MemberExpr* member_; | 651 MemberExpr* member_; |
608 FieldDecl* field_; | 652 FieldDecl* field_; |
609 }; | 653 }; |
610 | 654 |
611 // Nested checking for weak callbacks. | 655 // Nested checking for weak callbacks. |
612 CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} | 656 CheckTraceVisitor(RecordInfo* info) |
657 : trace_(nullptr), info_(info), cache_(nullptr) {} | |
613 | 658 |
614 bool IsWeakCallback() { return !trace_; } | 659 bool IsWeakCallback() { return !trace_; } |
615 | 660 |
616 void MarkTraced(RecordInfo::Fields::iterator it) { | 661 void MarkTraced(RecordInfo::Fields::iterator it) { |
617 // In a weak callback we can't mark strong fields as traced. | 662 // In a weak callback we can't mark strong fields as traced. |
618 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) | 663 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) |
619 return; | 664 return; |
620 it->second.MarkTraced(); | 665 it->second.MarkTraced(); |
621 } | 666 } |
622 | 667 |
(...skipping 13 matching lines...) Expand all Loading... | |
636 } | 681 } |
637 } else { | 682 } else { |
638 RecordInfo::Fields::iterator it = info_->GetFields().find(field); | 683 RecordInfo::Fields::iterator it = info_->GetFields().find(field); |
639 if (it != info_->GetFields().end()) | 684 if (it != info_->GetFields().end()) |
640 MarkTraced(it); | 685 MarkTraced(it); |
641 } | 686 } |
642 } | 687 } |
643 | 688 |
644 CXXMethodDecl* trace_; | 689 CXXMethodDecl* trace_; |
645 RecordInfo* info_; | 690 RecordInfo* info_; |
691 RecordCache* cache_; | |
646 bool delegates_to_traceimpl_; | 692 bool delegates_to_traceimpl_; |
647 }; | 693 }; |
648 | 694 |
649 // This visitor checks that the fields of a class and the fields of | 695 // This visitor checks that the fields of a class and the fields of |
650 // its part objects don't define GC roots. | 696 // its part objects don't define GC roots. |
651 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { | 697 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { |
652 public: | 698 public: |
653 typedef std::vector<FieldPoint*> RootPath; | 699 typedef std::vector<FieldPoint*> RootPath; |
654 typedef std::set<RecordInfo*> VisitingSet; | 700 typedef std::set<RecordInfo*> VisitingSet; |
655 typedef std::vector<RootPath> Errors; | 701 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) { | 1426 if (trace_type == Config::TRACE_METHOD) { |
1381 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1427 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
1382 it != parent->GetBases().end(); | 1428 it != parent->GetBases().end(); |
1383 ++it) { | 1429 ++it) { |
1384 RecordInfo* base = it->second.info(); | 1430 RecordInfo* base = it->second.info(); |
1385 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) | 1431 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
1386 ReportOverriddenNonVirtualTrace(parent, trace, other); | 1432 ReportOverriddenNonVirtualTrace(parent, trace, other); |
1387 } | 1433 } |
1388 } | 1434 } |
1389 | 1435 |
1390 CheckTraceVisitor visitor(trace, parent); | 1436 CheckTraceVisitor visitor(trace, parent, &cache_); |
1391 visitor.TraverseCXXMethodDecl(trace); | 1437 visitor.TraverseCXXMethodDecl(trace); |
1392 | 1438 |
1393 // Skip reporting if this trace method is a just delegate to | 1439 // Skip reporting if this trace method is a just delegate to |
1394 // traceImpl (or traceAfterDispatchImpl) method. We will report on | 1440 // traceImpl (or traceAfterDispatchImpl) method. We will report on |
1395 // CheckTraceMethod on traceImpl method. | 1441 // CheckTraceMethod on traceImpl method. |
1396 if (visitor.delegates_to_traceimpl()) | 1442 if (visitor.delegates_to_traceimpl()) |
1397 return; | 1443 return; |
1398 | 1444 |
1399 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1445 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
1400 it != parent->GetBases().end(); | 1446 it != parent->GetBases().end(); |
(...skipping 589 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1990 | 2036 |
1991 private: | 2037 private: |
1992 BlinkGCPluginOptions options_; | 2038 BlinkGCPluginOptions options_; |
1993 }; | 2039 }; |
1994 | 2040 |
1995 } // namespace | 2041 } // namespace |
1996 | 2042 |
1997 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 2043 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( |
1998 "blink-gc-plugin", | 2044 "blink-gc-plugin", |
1999 "Check Blink GC invariants"); | 2045 "Check Blink GC invariants"); |
OLD | NEW |