| Index: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
|
| diff --git a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
|
| index 2d221d7667a858c0d1ff8ec4bf1c7ca5b79f919b..4805bc646017adf14ba9042dbd304f8d54e83abe 100644
|
| --- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
|
| +++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
|
| @@ -8,6 +8,8 @@
|
| // Errors are described at:
|
| // http://www.chromium.org/developers/blink-gc-plugin-errors
|
|
|
| +#include <algorithm>
|
| +
|
| #include "Config.h"
|
| #include "JsonWriter.h"
|
| #include "RecordInfo.h"
|
| @@ -17,6 +19,7 @@
|
| #include "clang/AST/RecursiveASTVisitor.h"
|
| #include "clang/Frontend/CompilerInstance.h"
|
| #include "clang/Frontend/FrontendPluginRegistry.h"
|
| +#include "clang/Sema/Sema.h"
|
|
|
| using namespace clang;
|
| using std::string;
|
| @@ -56,6 +59,9 @@ const char kClassDoesNotRequireFinalization[] =
|
| const char kFinalizerAccessesFinalizedField[] =
|
| "[blink-gc] Finalizer %0 accesses potentially finalized field %1.";
|
|
|
| +const char kFinalizerAccessesEagerlyFinalizedField[] =
|
| + "[blink-gc] Finalizer %0 accesses eagerly finalized field %1.";
|
| +
|
| const char kRawPtrToGCManagedClassNote[] =
|
| "[blink-gc] Raw pointer field %0 to a GC managed class declared here:";
|
|
|
| @@ -65,6 +71,9 @@ const char kRefPtrToGCManagedClassNote[] =
|
| const char kOwnPtrToGCManagedClassNote[] =
|
| "[blink-gc] OwnPtr field %0 to a GC managed class declared here:";
|
|
|
| +const char kMemberToGCUnmanagedClassNote[] =
|
| + "[blink-gc] Member field %0 to non-GC managed class declared here:";
|
| +
|
| const char kStackAllocatedFieldNote[] =
|
| "[blink-gc] Stack-allocated field %0 declared here:";
|
|
|
| @@ -105,6 +114,9 @@ const char kMissingFinalizeDispatch[] =
|
| const char kFinalizedFieldNote[] =
|
| "[blink-gc] Potentially finalized field %0 declared here:";
|
|
|
| +const char kEagerlyFinalizedFieldNote[] =
|
| + "[blink-gc] Field %0 having eagerly finalized value, declared here:";
|
| +
|
| const char kUserDeclaredDestructorNote[] =
|
| "[blink-gc] User-declared destructor declared here:";
|
|
|
| @@ -144,6 +156,23 @@ const char kClassMustDeclareGCMixinTraceMethod[] =
|
| "[blink-gc] Class %0 which inherits from GarbageCollectedMixin must"
|
| " locally declare and override trace(Visitor*)";
|
|
|
| +// Use a local RAV implementation to simply collect all FunctionDecls marked for
|
| +// late template parsing. This happens with the flag -fdelayed-template-parsing,
|
| +// which is on by default in MSVC-compatible mode.
|
| +std::set<FunctionDecl*> GetLateParsedFunctionDecls(TranslationUnitDecl* decl) {
|
| + struct Visitor : public RecursiveASTVisitor<Visitor> {
|
| + bool VisitFunctionDecl(FunctionDecl* function_decl) {
|
| + if (function_decl->isLateTemplateParsed())
|
| + late_parsed_decls.insert(function_decl);
|
| + return true;
|
| + }
|
| +
|
| + std::set<FunctionDecl*> late_parsed_decls;
|
| + } v;
|
| + v.TraverseDecl(decl);
|
| + return v.late_parsed_decls;
|
| +}
|
| +
|
| struct BlinkGCPluginOptions {
|
| BlinkGCPluginOptions()
|
| : enable_oilpan(false)
|
| @@ -223,11 +252,27 @@ class CheckFinalizerVisitor
|
| // during finalization.
|
| class MightBeCollectedVisitor : public EdgeVisitor {
|
| public:
|
| - MightBeCollectedVisitor() : might_be_collected_(false) {}
|
| + MightBeCollectedVisitor(bool is_eagerly_finalized)
|
| + : might_be_collected_(false)
|
| + , is_eagerly_finalized_(is_eagerly_finalized)
|
| + , as_eagerly_finalized_(false) {}
|
| bool might_be_collected() { return might_be_collected_; }
|
| - void VisitMember(Member* edge) override { might_be_collected_ = true; }
|
| + bool as_eagerly_finalized() { return as_eagerly_finalized_; }
|
| + void VisitMember(Member* edge) override {
|
| + 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 VisitCollection(Collection* edge) override {
|
| - if (edge->on_heap()) {
|
| + if (edge->on_heap() && !is_eagerly_finalized_) {
|
| might_be_collected_ = !edge->is_root();
|
| } else {
|
| edge->AcceptMembers(this);
|
| @@ -236,13 +281,31 @@ class CheckFinalizerVisitor
|
|
|
| private:
|
| bool might_be_collected_;
|
| + bool is_eagerly_finalized_;
|
| + bool as_eagerly_finalized_;
|
| };
|
|
|
| public:
|
| - typedef std::vector<std::pair<MemberExpr*, FieldPoint*> > Errors;
|
| + class Error {
|
| + public:
|
| + Error(MemberExpr *member,
|
| + bool as_eagerly_finalized,
|
| + FieldPoint* field)
|
| + : member_(member)
|
| + , as_eagerly_finalized_(as_eagerly_finalized)
|
| + , field_(field) {}
|
| +
|
| + MemberExpr* member_;
|
| + bool as_eagerly_finalized_;
|
| + FieldPoint* field_;
|
| + };
|
| +
|
| + typedef std::vector<Error> Errors;
|
|
|
| - CheckFinalizerVisitor(RecordCache* cache)
|
| - : blacklist_context_(false), cache_(cache) {}
|
| + CheckFinalizerVisitor(RecordCache* cache, bool is_eagerly_finalized)
|
| + : blacklist_context_(false)
|
| + , cache_(cache)
|
| + , is_eagerly_finalized_(is_eagerly_finalized) {}
|
|
|
| Errors& finalized_fields() { return finalized_fields_; }
|
|
|
| @@ -280,21 +343,32 @@ class CheckFinalizerVisitor
|
| if (it == info->GetFields().end())
|
| return true;
|
|
|
| - if (blacklist_context_ && MightBeCollected(&it->second))
|
| - finalized_fields_.push_back(std::make_pair(member, &it->second));
|
| + 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 MightBeCollected(FieldPoint* point) {
|
| - MightBeCollectedVisitor visitor;
|
| + bool 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();
|
| }
|
|
|
| private:
|
| bool blacklist_context_;
|
| Errors finalized_fields_;
|
| + std::set<MemberExpr*> seen_members_;
|
| RecordCache* cache_;
|
| + bool is_eagerly_finalized_;
|
| };
|
|
|
| // This visitor checks that a method contains within its body, a call to a
|
| @@ -780,6 +854,7 @@ class CheckFieldsVisitor : public RecursiveEdgeVisitor {
|
| kRawPtrToGCManagedWarning,
|
| kRefPtrToGCManaged,
|
| kOwnPtrToGCManaged,
|
| + kMemberToGCUnmanaged,
|
| kMemberInUnmanaged,
|
| kPtrFromHeapToStack,
|
| kGCDerivedPartObject
|
| @@ -838,6 +913,23 @@ class CheckFieldsVisitor : public RecursiveEdgeVisitor {
|
| return;
|
| }
|
|
|
| + // If in a stack allocated context, be fairly insistent that T in Member<T>
|
| + // is GC allocated, as stack allocated objects do not have a trace()
|
| + // that separately verifies the validity of Member<T>.
|
| + //
|
| + // Notice that an error is only reported if T's definition is in scope;
|
| + // we do not require that it must be brought into scope as that would
|
| + // prevent declarations of mutually dependent class types.
|
| + //
|
| + // (Note: Member<>'s constructor will at run-time verify that the
|
| + // pointer it wraps is indeed heap allocated.)
|
| + if (stack_allocated_host_ && Parent() && Parent()->IsMember() &&
|
| + edge->value()->HasDefinition() && !edge->value()->IsGCAllocated()) {
|
| + invalid_fields_.push_back(std::make_pair(current_,
|
| + kMemberToGCUnmanaged));
|
| + return;
|
| + }
|
| +
|
| if (!Parent() || !edge->value()->IsGCAllocated())
|
| return;
|
|
|
| @@ -949,6 +1041,8 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| DiagnosticsEngine::Warning, kClassDoesNotRequireFinalization);
|
| diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID(
|
| getErrorLevel(), kFinalizerAccessesFinalizedField);
|
| + diag_finalizer_eagerly_finalized_field_ = diagnostic_.getCustomDiagID(
|
| + getErrorLevel(), kFinalizerAccessesEagerlyFinalizedField);
|
| diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID(
|
| getErrorLevel(), kOverriddenNonVirtualTrace);
|
| diag_missing_trace_dispatch_method_ = diagnostic_.getCustomDiagID(
|
| @@ -986,6 +1080,8 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| DiagnosticsEngine::Note, kRefPtrToGCManagedClassNote);
|
| diag_own_ptr_to_gc_managed_class_note_ = diagnostic_.getCustomDiagID(
|
| DiagnosticsEngine::Note, kOwnPtrToGCManagedClassNote);
|
| + diag_member_to_gc_unmanaged_class_note_ = diagnostic_.getCustomDiagID(
|
| + DiagnosticsEngine::Note, kMemberToGCUnmanagedClassNote);
|
| diag_stack_allocated_field_note_ = diagnostic_.getCustomDiagID(
|
| DiagnosticsEngine::Note, kStackAllocatedFieldNote);
|
| diag_member_in_unmanaged_class_note_ = diagnostic_.getCustomDiagID(
|
| @@ -998,6 +1094,8 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| DiagnosticsEngine::Note, kFieldContainsGCRootNote);
|
| diag_finalized_field_note_ = diagnostic_.getCustomDiagID(
|
| DiagnosticsEngine::Note, kFinalizedFieldNote);
|
| + diag_eagerly_finalized_field_note_ = diagnostic_.getCustomDiagID(
|
| + DiagnosticsEngine::Note, kEagerlyFinalizedFieldNote);
|
| diag_user_declared_destructor_note_ = diagnostic_.getCustomDiagID(
|
| DiagnosticsEngine::Note, kUserDeclaredDestructorNote);
|
| diag_user_declared_finalizer_note_ = diagnostic_.getCustomDiagID(
|
| @@ -1017,6 +1115,8 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| if (diagnostic_.hasErrorOccurred())
|
| return;
|
|
|
| + ParseFunctionTemplates(context.getTranslationUnitDecl());
|
| +
|
| CollectVisitor visitor;
|
| visitor.TraverseDecl(context.getTranslationUnitDecl());
|
|
|
| @@ -1063,6 +1163,31 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| }
|
| }
|
|
|
| + void ParseFunctionTemplates(TranslationUnitDecl* decl) {
|
| + if (!instance_.getLangOpts().DelayedTemplateParsing)
|
| + return; // Nothing to do.
|
| +
|
| + std::set<FunctionDecl*> late_parsed_decls =
|
| + GetLateParsedFunctionDecls(decl);
|
| + clang::Sema& sema = instance_.getSema();
|
| +
|
| + for (const FunctionDecl* fd : late_parsed_decls) {
|
| + assert(fd->isLateTemplateParsed());
|
| +
|
| + if (!Config::IsTraceMethod(fd))
|
| + continue;
|
| +
|
| + if (instance_.getSourceManager().isInSystemHeader(
|
| + instance_.getSourceManager().getSpellingLoc(fd->getLocation())))
|
| + continue;
|
| +
|
| + // Force parsing and AST building of the yet-uninstantiated function
|
| + // template trace method bodies.
|
| + clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd];
|
| + sema.LateTemplateParser(sema.OpaqueParser, *lpt);
|
| + }
|
| + }
|
| +
|
| // Main entry for checking a record declaration.
|
| void CheckRecord(RecordInfo* info) {
|
| if (IsIgnored(info))
|
| @@ -1340,7 +1465,7 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| // For finalized classes, check the finalization method if possible.
|
| if (info->IsGCFinalized()) {
|
| if (dtor && dtor->hasBody()) {
|
| - CheckFinalizerVisitor visitor(&cache_);
|
| + CheckFinalizerVisitor visitor(&cache_, info->IsEagerlyFinalized());
|
| visitor.TraverseCXXMethodDecl(dtor);
|
| if (!visitor.finalized_fields().empty()) {
|
| ReportFinalizerAccessesFinalizedFields(
|
| @@ -1610,6 +1735,9 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| string filename;
|
| if (!GetFilename(info->record()->getLocStart(), &filename))
|
| return false; // TODO: should we ignore non-existing file locations?
|
| +#if defined(LLVM_ON_WIN32)
|
| + std::replace(filename.begin(), filename.end(), '\\', '/');
|
| +#endif
|
| std::vector<string>::iterator it = options_.ignored_directories.begin();
|
| for (; it != options_.ignored_directories.end(); ++it)
|
| if (filename.find(*it) != string::npos)
|
| @@ -1727,6 +1855,8 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| error = diag_ref_ptr_to_gc_managed_class_note_;
|
| } else if (it->second == CheckFieldsVisitor::kOwnPtrToGCManaged) {
|
| error = diag_own_ptr_to_gc_managed_class_note_;
|
| + } else if (it->second == CheckFieldsVisitor::kMemberToGCUnmanaged) {
|
| + error = diag_member_to_gc_unmanaged_class_note_;
|
| } else if (it->second == CheckFieldsVisitor::kMemberInUnmanaged) {
|
| error = diag_member_in_unmanaged_class_note_;
|
| } else if (it->second == CheckFieldsVisitor::kPtrFromHeapToStack) {
|
| @@ -1766,12 +1896,19 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| for (CheckFinalizerVisitor::Errors::iterator it = fields->begin();
|
| it != fields->end();
|
| ++it) {
|
| - SourceLocation loc = it->first->getLocStart();
|
| + SourceLocation loc = it->member_->getLocStart();
|
| SourceManager& manager = instance_.getSourceManager();
|
| + bool as_eagerly_finalized = it->as_eagerly_finalized_;
|
| + unsigned diag_error = as_eagerly_finalized ?
|
| + diag_finalizer_eagerly_finalized_field_ :
|
| + diag_finalizer_accesses_finalized_field_;
|
| + unsigned diag_note = as_eagerly_finalized ?
|
| + diag_eagerly_finalized_field_note_ :
|
| + diag_finalized_field_note_;
|
| 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_);
|
| + diagnostic_.Report(full_loc, diag_error)
|
| + << dtor << it->field_->field();
|
| + NoteField(it->field_, diag_note);
|
| }
|
| }
|
|
|
| @@ -1980,6 +2117,7 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| unsigned diag_class_requires_finalization_;
|
| unsigned diag_class_does_not_require_finalization_;
|
| unsigned diag_finalizer_accesses_finalized_field_;
|
| + unsigned diag_finalizer_eagerly_finalized_field_;
|
| unsigned diag_overridden_non_virtual_trace_;
|
| unsigned diag_missing_trace_dispatch_method_;
|
| unsigned diag_missing_finalize_dispatch_method_;
|
| @@ -1998,12 +2136,14 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| unsigned diag_raw_ptr_to_gc_managed_class_note_;
|
| unsigned diag_ref_ptr_to_gc_managed_class_note_;
|
| unsigned diag_own_ptr_to_gc_managed_class_note_;
|
| + unsigned diag_member_to_gc_unmanaged_class_note_;
|
| unsigned diag_stack_allocated_field_note_;
|
| unsigned diag_member_in_unmanaged_class_note_;
|
| unsigned diag_part_object_to_gc_derived_class_note_;
|
| unsigned diag_part_object_contains_gc_root_note_;
|
| unsigned diag_field_contains_gc_root_note_;
|
| unsigned diag_finalized_field_note_;
|
| + unsigned diag_eagerly_finalized_field_note_;
|
| unsigned diag_user_declared_destructor_note_;
|
| unsigned diag_user_declared_finalizer_note_;
|
| unsigned diag_base_requires_finalization_note_;
|
|
|