summaryrefslogtreecommitdiffstats
path: root/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp')
-rw-r--r--tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp161
1 files changed, 144 insertions, 17 deletions
diff --git a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
index 800b316..479e1ac 100644
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
@@ -41,6 +41,12 @@ const char kClassContainsInvalidFields[] =
const char kClassContainsGCRoot[] =
"[blink-gc] Class %0 contains GC root in field %1.";
+const char kFinalizerInNonFinalizedClass[] =
+ "[blink-gc] Non-finalized class %0 has a user-declared finalizer %1.";
+
+const char kFinalizerAccessesFinalizedField[] =
+ "[blink-gc] Finalizer %0 accesses potentially finalized field %1.";
+
const char kRawPtrToGCManagedClassNote[] =
"[blink-gc] Raw pointer field %0 to a GC managed class declared here:";
@@ -56,6 +62,9 @@ const char kPartObjectContainsGCRoot[] =
const char kFieldContainsGCRoot[] =
"[blink-gc] Field %0 defining a GC root declared here:";
+const char kFinalizedFieldNote[] =
+ "[blink-gc] Potentially finalized field %0 declared here:";
+
struct BlinkGCPluginOptions {
BlinkGCPluginOptions() : enable_oilpan(false) {}
bool enable_oilpan;
@@ -117,6 +126,67 @@ class CollectVisitor : public RecursiveASTVisitor<CollectVisitor> {
MethodVector trace_decls_;
};
+// This visitor checks that a finalizer method does not access fields that are
+// potentially finalized. A potentially finalized field is either a Member, a
+// heap-allocated collection or an off-heap collection that contains Members.
+class CheckFinalizerVisitor
+ : public RecursiveASTVisitor<CheckFinalizerVisitor> {
+ private:
+ // Simple visitor to determine if the content of a field might be collected
+ // during finalization.
+ class MightBeCollectedVisitor : public EdgeVisitor {
+ public:
+ MightBeCollectedVisitor() : might_be_collected_(false) {}
+ bool might_be_collected() { return might_be_collected_; }
+ void VisitMember(Member* edge) { might_be_collected_ = true; }
+ void VisitCollection(Collection* edge) {
+ if (edge->on_heap()) {
+ might_be_collected_ = !edge->is_root();
+ } else {
+ edge->AcceptMembers(this);
+ }
+ }
+
+ private:
+ bool might_be_collected_;
+ };
+
+ public:
+ typedef std::vector<std::pair<MemberExpr*, FieldPoint*> > Errors;
+
+ CheckFinalizerVisitor(RecordCache* cache) : cache_(cache) {}
+
+ Errors& finalized_fields() { return finalized_fields_; }
+
+ bool 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 (MightBeCollected(&it->second))
+ finalized_fields_.push_back(std::make_pair(member, &it->second));
+ return true;
+ }
+
+ bool MightBeCollected(FieldPoint* point) {
+ MightBeCollectedVisitor visitor;
+ point->edge()->Accept(&visitor);
+ return visitor.might_be_collected();
+ }
+
+ private:
+ Errors finalized_fields_;
+ RecordCache* cache_;
+};
+
// This visitor checks a tracing method by traversing its body.
// - A member field is considered traced if it is referenced in the body.
// - A base is traced if a base-qualified call to a trace method is found.
@@ -339,6 +409,10 @@ class BlinkGCPluginConsumer : public ASTConsumer {
kClassContainsInvalidFields);
diag_class_contains_gc_root_ =
diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot);
+ diag_finalizer_in_nonfinalized_class_ = diagnostic_.getCustomDiagID(
+ getErrorLevel(), kFinalizerInNonFinalizedClass);
+ diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID(
+ getErrorLevel(), kFinalizerAccessesFinalizedField);
// Register note messages.
diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID(
@@ -353,6 +427,8 @@ class BlinkGCPluginConsumer : public ASTConsumer {
DiagnosticsEngine::Note, kPartObjectContainsGCRoot);
diag_field_contains_gc_root_note_ = diagnostic_.getCustomDiagID(
DiagnosticsEngine::Note, kFieldContainsGCRoot);
+ diag_finalized_field_note_ = diagnostic_.getCustomDiagID(
+ DiagnosticsEngine::Note, kFinalizedFieldNote);
}
virtual void HandleTranslationUnit(ASTContext& context) {
@@ -414,9 +490,37 @@ class BlinkGCPluginConsumer : public ASTConsumer {
}
if (info->IsGCDerived()) {
- CheckGCRootsVisitor visitor;
- if (visitor.ContainsGCRoots(info))
- ReportClassContainsGCRoots(info, &visitor.gc_roots());
+ {
+ CheckGCRootsVisitor visitor;
+ if (visitor.ContainsGCRoots(info))
+ ReportClassContainsGCRoots(info, &visitor.gc_roots());
+ }
+
+ // TODO: check for non-user defined and non-trivial destructors too.
+ // TODO: support overridden finalize().
+ if (CXXDestructorDecl* dtor = info->record()->getDestructor()) {
+ if (dtor->isUserProvided() && !info->IsGCFinalized()) {
+ // Don't report if using transition types and the body is empty.
+ if (!options_.enable_oilpan) {
+ ReportFinalizerInNonFinalizedClass(info, dtor);
+ } else {
+ if (dtor->hasBody()) {
+ CompoundStmt* stmt = cast<CompoundStmt>(dtor->getBody());
+ if (stmt && !stmt->body_empty())
+ ReportFinalizerInNonFinalizedClass(info, dtor);
+ }
+ }
+ }
+
+ if (dtor->hasBody()) {
+ CheckFinalizerVisitor visitor(&cache_);
+ visitor.TraverseCXXMethodDecl(dtor);
+ if (!visitor.finalized_fields().empty()) {
+ ReportFinalizerAccessesFinalizedFields(dtor,
+ &visitor.finalized_fields());
+ }
+ }
+ }
}
}
@@ -603,11 +707,11 @@ class BlinkGCPluginConsumer : public ASTConsumer {
it != errors->end();
++it) {
if (it->second->IsRawPtr()) {
- NoteInvalidField(it->first, diag_raw_ptr_to_gc_managed_class_note_);
+ NoteField(it->first, diag_raw_ptr_to_gc_managed_class_note_);
} else if (it->second->IsRefPtr()) {
- NoteInvalidField(it->first, diag_ref_ptr_to_gc_managed_class_note_);
+ NoteField(it->first, diag_ref_ptr_to_gc_managed_class_note_);
} else if (it->second->IsOwnPtr()) {
- NoteInvalidField(it->first, diag_own_ptr_to_gc_managed_class_note_);
+ NoteField(it->first, diag_own_ptr_to_gc_managed_class_note_);
}
}
}
@@ -632,18 +736,32 @@ class BlinkGCPluginConsumer : public ASTConsumer {
}
}
- void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) {
- SourceLocation loc = field->getLocStart();
+ void ReportFinalizerInNonFinalizedClass(RecordInfo* info,
+ CXXMethodDecl* dtor) {
+ SourceLocation loc = dtor->getLocStart();
SourceManager& manager = instance_.getSourceManager();
FullSourceLoc full_loc(loc, manager);
- diagnostic_.Report(full_loc, diag_field_requires_tracing_note_) << field;
+ diagnostic_.Report(full_loc, diag_finalizer_in_nonfinalized_class_)
+ << info->record() << dtor;
}
- void NoteInvalidField(FieldPoint* point, unsigned note) {
- SourceLocation loc = point->field()->getLocStart();
- SourceManager& manager = instance_.getSourceManager();
- FullSourceLoc full_loc(loc, manager);
- diagnostic_.Report(full_loc, note) << point->field();
+ void ReportFinalizerAccessesFinalizedFields(
+ CXXMethodDecl* dtor,
+ CheckFinalizerVisitor::Errors* fields) {
+ for (CheckFinalizerVisitor::Errors::iterator it = fields->begin();
+ it != fields->end();
+ ++it) {
+ SourceLocation loc = it->first->getLocStart();
+ SourceManager& manager = instance_.getSourceManager();
+ FullSourceLoc full_loc(loc, manager);
+ diagnostic_.Report(full_loc, diag_finalizer_accesses_finalized_field_)
+ << dtor << it->second->field();
+ NoteField(it->second, diag_finalized_field_note_);
+ }
+ }
+
+ void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) {
+ NoteField(field, diag_field_requires_tracing_note_);
}
void NotePartObjectContainsGCRoot(FieldPoint* point) {
@@ -656,12 +774,18 @@ class BlinkGCPluginConsumer : public ASTConsumer {
}
void NoteFieldContainsGCRoot(FieldPoint* point) {
- FieldDecl* field = point->field();
+ NoteField(point, diag_field_contains_gc_root_note_);
+ }
+
+ void NoteField(FieldPoint* point, unsigned note) {
+ NoteField(point->field(), note);
+ }
+
+ void NoteField(FieldDecl* field, unsigned note) {
SourceLocation loc = field->getLocStart();
SourceManager& manager = instance_.getSourceManager();
FullSourceLoc full_loc(loc, manager);
- diagnostic_.Report(full_loc, diag_field_contains_gc_root_note_)
- << field;
+ diagnostic_.Report(full_loc, note) << field;
}
unsigned diag_class_requires_trace_method_;
@@ -669,6 +793,8 @@ class BlinkGCPluginConsumer : public ASTConsumer {
unsigned diag_fields_require_tracing_;
unsigned diag_class_contains_invalid_fields_;
unsigned diag_class_contains_gc_root_;
+ unsigned diag_finalizer_in_nonfinalized_class_;
+ unsigned diag_finalizer_accesses_finalized_field_;
unsigned diag_field_requires_tracing_note_;
unsigned diag_raw_ptr_to_gc_managed_class_note_;
@@ -676,6 +802,7 @@ class BlinkGCPluginConsumer : public ASTConsumer {
unsigned diag_own_ptr_to_gc_managed_class_note_;
unsigned diag_part_object_contains_gc_root_note_;
unsigned diag_field_contains_gc_root_note_;
+ unsigned diag_finalized_field_note_;
CompilerInstance& instance_;
DiagnosticsEngine& diagnostic_;