1 // Copyright 2015 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 "CheckFinalizerVisitor.h"
6 
7 using namespace clang;
8 
9 namespace {
10 
11 // Simple visitor to determine if the content of a field might be collected
12 // during finalization.
13 class MightBeCollectedVisitor : public EdgeVisitor {
14  public:
15   bool might_be_collected() const;
16 
17   void VisitMember(Member* edge) override;
18   void VisitCollection(Collection* edge) override;
19 
20  private:
21   bool might_be_collected_ = false;
22 };
23 
might_be_collected() const24 bool MightBeCollectedVisitor::might_be_collected() const {
25   return might_be_collected_;
26 }
27 
VisitMember(Member * edge)28 void MightBeCollectedVisitor::VisitMember(Member* edge) {
29   might_be_collected_ = true;
30 }
31 
VisitCollection(Collection * edge)32 void MightBeCollectedVisitor::VisitCollection(Collection* edge) {
33   if (edge->on_heap()) {
34     might_be_collected_ = true;
35   } else {
36     edge->AcceptMembers(this);
37   }
38 }
39 
40 }  // namespace
41 
CheckFinalizerVisitor(RecordCache * cache)42 CheckFinalizerVisitor::CheckFinalizerVisitor(RecordCache* cache)
43     : blacklist_context_(false),
44       cache_(cache) {
45 }
46 
finalized_fields()47 CheckFinalizerVisitor::Errors& CheckFinalizerVisitor::finalized_fields() {
48   return finalized_fields_;
49 }
50 
WalkUpFromCXXOperatorCallExpr(CXXOperatorCallExpr * expr)51 bool CheckFinalizerVisitor::WalkUpFromCXXOperatorCallExpr(
52     CXXOperatorCallExpr* expr) {
53   // Only continue the walk-up if the operator is a blacklisted one.
54   switch (expr->getOperator()) {
55     case OO_Arrow:
56     case OO_Subscript:
57       this->WalkUpFromCallExpr(expr);
58       return true;
59     default:
60       return true;
61   }
62 }
63 
WalkUpFromCallExpr(CallExpr * expr)64 bool CheckFinalizerVisitor::WalkUpFromCallExpr(CallExpr* expr) {
65   // We consider all non-operator calls to be blacklisted contexts.
66   bool prev_blacklist_context = blacklist_context_;
67   blacklist_context_ = true;
68   for (size_t i = 0; i < expr->getNumArgs(); ++i)
69     this->TraverseStmt(expr->getArg(i));
70   blacklist_context_ = prev_blacklist_context;
71   return true;
72 }
73 
VisitMemberExpr(MemberExpr * member)74 bool CheckFinalizerVisitor::VisitMemberExpr(MemberExpr* member) {
75   FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl());
76   if (!field)
77     return true;
78 
79   RecordInfo* info = cache_->Lookup(field->getParent());
80   if (!info)
81     return true;
82 
83   RecordInfo::Fields::iterator it = info->GetFields().find(field);
84   if (it == info->GetFields().end())
85     return true;
86 
87   if (seen_members_.find(member) != seen_members_.end())
88     return true;
89 
90   if (blacklist_context_ &&
91       MightBeCollected(&it->second)) {
92     finalized_fields_.push_back(
93         Error(member, &it->second));
94     seen_members_.insert(member);
95   }
96   return true;
97 }
98 
MightBeCollected(FieldPoint * point)99 bool CheckFinalizerVisitor::MightBeCollected(FieldPoint* point) {
100   MightBeCollectedVisitor visitor;
101   point->edge()->Accept(&visitor);
102   return visitor.might_be_collected();
103 }
104