| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "CheckFinalizerVisitor.h" |
| |
| using namespace clang; |
| |
| namespace { |
| |
| // Simple visitor to determine if the content of a field might be collected |
| // during finalization. |
| class MightBeCollectedVisitor : public EdgeVisitor { |
| public: |
| explicit MightBeCollectedVisitor(bool is_eagerly_finalized); |
| |
| bool might_be_collected() const; |
| bool as_eagerly_finalized() const; |
| |
| void VisitMember(Member* edge) override; |
| void VisitCollection(Collection* edge) override; |
| |
| private: |
| bool might_be_collected_; |
| bool is_eagerly_finalized_; |
| bool as_eagerly_finalized_; |
| }; |
| |
| MightBeCollectedVisitor::MightBeCollectedVisitor(bool is_eagerly_finalized) |
| : might_be_collected_(false), |
| is_eagerly_finalized_(is_eagerly_finalized), |
| as_eagerly_finalized_(false) { |
| } |
| |
| bool MightBeCollectedVisitor::might_be_collected() const { |
| return might_be_collected_; |
| } |
| |
| bool MightBeCollectedVisitor::as_eagerly_finalized() const { |
| return as_eagerly_finalized_; |
| } |
| |
| void MightBeCollectedVisitor::VisitMember(Member* edge) { |
| if (is_eagerly_finalized_) { |
| if (edge->ptr()->IsValue()) { |
| Value* member = static_cast<Value*>(edge->ptr()); |
| if (member->value()->IsEagerlyFinalized()) { |
| might_be_collected_ = true; |
| as_eagerly_finalized_ = true; |
| } |
| } |
| return; |
| } |
| might_be_collected_ = true; |
| } |
| |
| void MightBeCollectedVisitor::VisitCollection(Collection* edge) { |
| if (edge->on_heap() && !is_eagerly_finalized_) { |
| might_be_collected_ = !edge->is_root(); |
| } else { |
| edge->AcceptMembers(this); |
| } |
| } |
| |
| } // namespace |
| |
| CheckFinalizerVisitor::CheckFinalizerVisitor(RecordCache* cache, |
| bool is_eagerly_finalized) |
| : blacklist_context_(false), |
| cache_(cache), |
| is_eagerly_finalized_(is_eagerly_finalized) { |
| } |
| |
| CheckFinalizerVisitor::Errors& CheckFinalizerVisitor::finalized_fields() { |
| return finalized_fields_; |
| } |
| |
| bool CheckFinalizerVisitor::WalkUpFromCXXOperatorCallExpr( |
| CXXOperatorCallExpr* expr) { |
| // Only continue the walk-up if the operator is a blacklisted one. |
| switch (expr->getOperator()) { |
| case OO_Arrow: |
| case OO_Subscript: |
| this->WalkUpFromCallExpr(expr); |
| return true; |
| default: |
| return true; |
| } |
| } |
| |
| bool CheckFinalizerVisitor::WalkUpFromCallExpr(CallExpr* expr) { |
| // We consider all non-operator calls to be blacklisted contexts. |
| bool prev_blacklist_context = blacklist_context_; |
| blacklist_context_ = true; |
| for (size_t i = 0; i < expr->getNumArgs(); ++i) |
| this->TraverseStmt(expr->getArg(i)); |
| blacklist_context_ = prev_blacklist_context; |
| return true; |
| } |
| |
| bool CheckFinalizerVisitor::VisitMemberExpr(MemberExpr* member) { |
| FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl()); |
| if (!field) |
| return true; |
| |
| RecordInfo* info = cache_->Lookup(field->getParent()); |
| if (!info) |
| return true; |
| |
| RecordInfo::Fields::iterator it = info->GetFields().find(field); |
| if (it == info->GetFields().end()) |
| return true; |
| |
| if (seen_members_.find(member) != seen_members_.end()) |
| return true; |
| |
| bool as_eagerly_finalized = false; |
| if (blacklist_context_ && |
| MightBeCollected(&it->second, &as_eagerly_finalized)) { |
| finalized_fields_.push_back( |
| Error(member, as_eagerly_finalized, &it->second)); |
| seen_members_.insert(member); |
| } |
| return true; |
| } |
| |
| bool CheckFinalizerVisitor::MightBeCollected(FieldPoint* point, |
| bool* as_eagerly_finalized) { |
| MightBeCollectedVisitor visitor(is_eagerly_finalized_); |
| point->edge()->Accept(&visitor); |
| *as_eagerly_finalized = visitor.as_eagerly_finalized(); |
| return visitor.might_be_collected(); |
| } |