Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Unified Diff: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp

Issue 455263004: Blink GC plugin: Ensure that classes with virtual trace methods always have vtables for their left-… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: weaker requirments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Config.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 0ad2ff6b7d4d621852ea716fcd0897bf42ba5a9a..31e3573f1e56c42d3890171f04b944543450cb37 100644
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
@@ -129,6 +129,14 @@ const char kClassDeclaresPureVirtualTrace[] =
"[blink-gc] Garbage collected class %0"
" is not permitted to declare a pure-virtual trace method.";
+const char kLeftMostBaseMustBePolymorphic[] =
+ "[blink-gc] Left-most base class %0 of derived class %1"
+ " must be polymorphic.";
+
+const char kBaseClassMustDeclareVirtualTrace[] =
+ "[blink-gc] Left-most base class %0 of derived class %1"
+ " must define a virtual trace method.";
+
struct BlinkGCPluginOptions {
BlinkGCPluginOptions() : enable_oilpan(false), dump_graph(false) {}
bool enable_oilpan;
@@ -771,6 +779,10 @@ class BlinkGCPluginConsumer : public ASTConsumer {
diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew);
diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID(
getErrorLevel(), kClassDeclaresPureVirtualTrace);
+ diag_left_most_base_must_be_polymorphic_ = diagnostic_.getCustomDiagID(
+ getErrorLevel(), kLeftMostBaseMustBePolymorphic);
+ diag_base_class_must_declare_virtual_trace_ = diagnostic_.getCustomDiagID(
+ getErrorLevel(), kBaseClassMustDeclareVirtualTrace);
// Register note messages.
diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID(
@@ -900,10 +912,13 @@ class BlinkGCPluginConsumer : public ASTConsumer {
if (CXXMethodDecl* trace = info->GetTraceMethod()) {
if (trace->isPure())
ReportClassDeclaresPureVirtualTrace(info, trace);
+ if (info->record()->isPolymorphic())
+ CheckPolymorphicClass(info, trace);
} else if (info->RequiresTraceMethod()) {
ReportClassRequiresTraceMethod(info);
}
+
Mads Ager (chromium) 2014/08/13 12:23:58 Remove extra empty line?
zerny-chromium 2014/08/13 12:50:35 Done.
{
CheckFieldsVisitor visitor(options_);
if (visitor.ContainsInvalidFields(info))
@@ -932,6 +947,121 @@ class BlinkGCPluginConsumer : public ASTConsumer {
DumpClass(info);
}
+ CXXRecordDecl* GetDependentTemplatedDecl(const Type& type) {
+ const TemplateSpecializationType* tmpl_type =
+ type.getAs<TemplateSpecializationType>();
+ if (!tmpl_type)
+ return 0;
+
+ TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl();
+ if (!tmpl_decl)
+ return 0;
+
+ return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl());
+ }
+
+ // The GC infrastructure assumes that if the vtable of a polymorphic
+ // base-class is not initialized for a given object (ie, it is partially
+ // initialized) then the object does not need to be traced. Thus, we must
+ // ensure that any polymorphic class with a trace method does not have any
+ // tractable fields that are initialized before we are sure that the vtable
+ // and the trace method are both defined. There are two cases that need to
+ // hold to satisfy that assumption:
+ //
+ // 1. If trace is virtual, then it must be defined in the left-most base.
+ // This ensures that if the vtable is initialized and it contains a pointer to
+ // the trace method.
+ //
+ // 2. If trace is non-virtual, then the trace method is defined and we must
+ // ensure that the left-most base defines a vtable. This ensures that the
+ // first thing to be initialized when constructing the object is the vtable
+ // itself.
+ void CheckPolymorphicClass(RecordInfo* info, CXXMethodDecl* trace) {
+ CXXRecordDecl* left_most = info->record();
+ CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
+ CXXRecordDecl* left_most_base = 0;
+ while (it != left_most->bases_end()) {
+ left_most_base = it->getType()->getAsCXXRecordDecl();
+ if (!left_most_base && it->getType()->isDependentType())
+ left_most_base = GetDependentTemplatedDecl(*it->getType());
+
+ // TODO: Find a way to correctly check actual instantiations
+ // for dependent types. The escape below will be hit, eg, when
+ // we have a primary template with no definition and
+ // specializations for each case (such as SupplementBase) in
+ // which case we don't succeed in checking the required
+ // properties.
+ if (!left_most_base || !left_most_base->hasDefinition())
+ return;
+
+ StringRef name = left_most_base->getName();
+ // We know GCMixin base defines virtual trace.
+ if (Config::IsGCMixinBase(name))
+ return;
+
+ // Stop with the left-most prior to a safe polymorphic base, ie, the a
Mads Ager (chromium) 2014/08/13 12:23:58 the a -> the
zerny-chromium 2014/08/13 12:50:35 Also removed the ambiguity of "the base".
+ // base that is non-polymorphic and contains no fields that need tracing.
+ if (Config::IsSafePolymorphicBase(name))
+ break;
+
+ left_most = left_most_base;
+ it = left_most->bases_begin();
+ }
+
+ if (RecordInfo* left_most_info = cache_.Lookup(left_most)) {
+
+ // Check condition (1):
+ if (trace->isVirtual()) {
+ if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) {
+ if (trace->isVirtual())
+ return;
+ }
+ ReportBaseClassMustDeclareVirtualTrace(info, left_most);
+ return;
+ }
+
+ // Check condition (2):
+ if (DeclaresVirtualMethods(info->record()))
+ return;
+ if (left_most_base) {
+ ++it; // Get the base next to the "safe polymorphic base"
+ if (it != left_most->bases_end()) {
+ if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) {
+ if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) {
+ if (DeclaresVirtualMethods(next_left_most))
+ return;
+ ReportLeftMostBaseMustBePolymorphic(info, next_left_most);
+ return;
+ }
+ }
+ }
+ }
+ ReportLeftMostBaseMustBePolymorphic(info, left_most);
+ }
+ }
+
+ CXXRecordDecl* GetLeftMostBase(CXXRecordDecl* left_most) {
+ CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
+ while (it != left_most->bases_end()) {
+ if (it->getType()->isDependentType())
+ left_most = GetDependentTemplatedDecl(*it->getType());
+ else
+ left_most = it->getType()->getAsCXXRecordDecl();
+ if (!left_most || !left_most->hasDefinition())
+ return 0;
+ it = left_most->bases_begin();
+ }
+ return left_most;
+ }
+
+ bool DeclaresVirtualMethods(CXXRecordDecl* decl) {
+ CXXRecordDecl::method_iterator it = decl->method_begin();
+ for (; it != decl->method_end(); ++it)
+ if (it->isVirtual() && !it->isPure())
+ return true;
+ return false;
+ }
+
void CheckLeftMostDerived(RecordInfo* info) {
CXXRecordDecl* left_most = info->record();
CXXRecordDecl::base_class_iterator it = left_most->bases_begin();
@@ -1074,16 +1204,12 @@ class BlinkGCPluginConsumer : public ASTConsumer {
void CheckTraceMethod(RecordInfo* parent,
CXXMethodDecl* trace,
bool isTraceAfterDispatch) {
- // A non-virtual trace method must not override another trace.
- if (!isTraceAfterDispatch && !trace->isVirtual()) {
+ // A trace method must not override any non-virtual trace methods.
+ if (!isTraceAfterDispatch) {
for (RecordInfo::Bases::iterator it = parent->GetBases().begin();
it != parent->GetBases().end();
++it) {
RecordInfo* base = it->second.info();
- // We allow mixin bases to contain a non-virtual trace since it will
- // never be used for dispatching.
- if (base->IsGCMixin())
- continue;
if (CXXMethodDecl* other = base->InheritsNonVirtualTrace())
ReportOverriddenNonVirtualTrace(parent, trace, other);
}
@@ -1484,6 +1610,24 @@ class BlinkGCPluginConsumer : public ASTConsumer {
<< info->record();
}
+ void ReportLeftMostBaseMustBePolymorphic(RecordInfo* derived,
+ CXXRecordDecl* base) {
+ SourceLocation loc = base->getLocStart();
+ SourceManager& manager = instance_.getSourceManager();
+ FullSourceLoc full_loc(loc, manager);
+ diagnostic_.Report(full_loc, diag_left_most_base_must_be_polymorphic_)
+ << base << derived->record();
+ }
+
+ void ReportBaseClassMustDeclareVirtualTrace(RecordInfo* derived,
+ CXXRecordDecl* base) {
+ SourceLocation loc = base->getLocStart();
+ SourceManager& manager = instance_.getSourceManager();
+ FullSourceLoc full_loc(loc, manager);
+ diagnostic_.Report(full_loc, diag_base_class_must_declare_virtual_trace_)
+ << base << derived->record();
+ }
+
void NoteManualDispatchMethod(CXXMethodDecl* dispatch) {
SourceLocation loc = dispatch->getLocStart();
SourceManager& manager = instance_.getSourceManager();
@@ -1574,6 +1718,8 @@ class BlinkGCPluginConsumer : public ASTConsumer {
unsigned diag_derives_non_stack_allocated_;
unsigned diag_class_overrides_new_;
unsigned diag_class_declares_pure_virtual_trace_;
+ unsigned diag_left_most_base_must_be_polymorphic_;
+ unsigned diag_base_class_must_declare_virtual_trace_;
unsigned diag_base_requires_tracing_note_;
unsigned diag_field_requires_tracing_note_;
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Config.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698