Index: tools/clang/blink_gc_plugin/CheckFinalizerVisitor.cpp |
diff --git a/tools/clang/blink_gc_plugin/CheckFinalizerVisitor.cpp b/tools/clang/blink_gc_plugin/CheckFinalizerVisitor.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f0a9bf3dba0a15198331c5a862c8fcfefceed212 |
--- /dev/null |
+++ b/tools/clang/blink_gc_plugin/CheckFinalizerVisitor.cpp |
@@ -0,0 +1,133 @@ |
+// 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(); |
+} |