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 43fbcf83d5fe1ab6bb1e45edf5b44f7e3c970300..0ad2ff6b7d4d621852ea716fcd0897bf42ba5a9a 100644 |
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
@@ -307,70 +307,187 @@ class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) |
: trace_(trace), info_(info) {} |
- // Allow recursive traversal by using VisitMemberExpr. |
bool VisitMemberExpr(MemberExpr* member) { |
- // If this member expression references a field decl, mark it as traced. |
- if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { |
- if (IsTemplateInstantiation(info_->record())) { |
- // Pointer equality on fields does not work for template instantiations. |
- // The trace method refers to fields of the template definition which |
- // are different from the instantiated fields that need to be traced. |
- const string& name = field->getNameAsString(); |
+ // In weak callbacks, consider any occurrence as a correct usage. |
+ // TODO: We really want to require that isAlive is checked on manually |
+ // processed weak fields. |
+ if (IsWeakCallback()) { |
+ if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) |
+ FoundField(field); |
+ } |
+ return true; |
+ } |
+ |
+ bool VisitCallExpr(CallExpr* call) { |
+ // In weak callbacks we don't check calls (see VisitMemberExpr). |
+ if (IsWeakCallback()) |
+ return true; |
+ |
+ Expr* callee = call->getCallee(); |
+ |
+ // Trace calls from a templated derived class result in a |
+ // DependentScopeMemberExpr because the concrete trace call depends on the |
+ // instantiation of any shared template parameters. In this case the call is |
+ // "unresolved" and we resort to comparing the syntactic type names. |
+ if (CXXDependentScopeMemberExpr* expr = |
+ dyn_cast<CXXDependentScopeMemberExpr>(callee)) { |
+ CheckCXXDependentScopeMemberExpr(call, expr); |
+ return true; |
+ } |
+ |
+ // A tracing call will have either a |visitor| or a |m_field| argument. |
+ // A registerWeakMembers call will have a |this| argument. |
+ if (call->getNumArgs() != 1) |
+ return true; |
+ Expr* arg = call->getArg(0); |
+ |
+ if (UnresolvedMemberExpr* expr = dyn_cast<UnresolvedMemberExpr>(callee)) { |
+ // If we find a call to registerWeakMembers which is unresolved we |
+ // unsoundly consider all weak members as traced. |
+ // TODO: Find out how to validate weak member tracing for unresolved call. |
+ if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) { |
for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); |
it != info_->GetFields().end(); |
++it) { |
- if (it->first->getNameAsString() == name) { |
- MarkTraced(it); |
- break; |
- } |
+ if (it->second.edge()->IsWeakMember()) |
+ it->second.MarkTraced(); |
} |
- } else { |
- RecordInfo::Fields::iterator it = info_->GetFields().find(field); |
- if (it != info_->GetFields().end()) |
- MarkTraced(it); |
} |
- return true; |
- } |
- // If this is a weak callback function we only check field tracing. |
- if (IsWeakCallback()) |
+ QualType base = expr->getBaseType(); |
+ if (!base->isPointerType()) |
+ return true; |
+ CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); |
+ if (decl) |
+ CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); |
return true; |
+ } |
- // For method calls, check tracing of bases and other special GC methods. |
- if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) { |
- const string& name = fn->getNameAsString(); |
- // Check weak callbacks. |
- if (name == kRegisterWeakMembersName) { |
- if (fn->isTemplateInstantiation()) { |
- const TemplateArgumentList& args = |
- *fn->getTemplateSpecializationInfo()->TemplateArguments; |
- // The second template argument is the callback method. |
- if (args.size() > 1 && |
- args[1].getKind() == TemplateArgument::Declaration) { |
- if (FunctionDecl* callback = |
- dyn_cast<FunctionDecl>(args[1].getAsDecl())) { |
- if (callback->hasBody()) { |
- CheckTraceVisitor nested_visitor(info_); |
- nested_visitor.TraverseStmt(callback->getBody()); |
- } |
- } |
- } |
- } |
+ if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { |
+ if (CheckTraceFieldCall(expr) || CheckRegisterWeakMembers(expr)) |
return true; |
+ } |
+ |
+ CheckTraceBaseCall(call); |
+ return true; |
+ } |
+ |
+ private: |
+ |
+ CXXRecordDecl* GetDependentTemplatedDecl(CXXDependentScopeMemberExpr* expr) { |
+ NestedNameSpecifier* qual = expr->getQualifier(); |
+ if (!qual) |
+ return 0; |
+ |
+ const Type* type = qual->getAsType(); |
+ if (!type) |
+ return 0; |
+ |
+ 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()); |
+ } |
+ |
+ void CheckCXXDependentScopeMemberExpr(CallExpr* call, |
+ CXXDependentScopeMemberExpr* expr) { |
+ string fn_name = expr->getMember().getAsString(); |
+ CXXRecordDecl* tmpl = GetDependentTemplatedDecl(expr); |
+ if (!tmpl) |
+ return; |
+ |
+ // Check for Super<T>::trace(visitor) |
+ if (call->getNumArgs() == 1 && fn_name == trace_->getName()) { |
+ RecordInfo::Bases::iterator it = info_->GetBases().begin(); |
+ for (; it != info_->GetBases().end(); ++it) { |
+ if (it->first->getName() == tmpl->getName()) |
+ it->second.MarkTraced(); |
} |
+ return; |
+ } |
- // Currently, a manually dispatched class cannot have mixin bases (having |
- // one would add a vtable which we explicitly check against). This means |
- // that we can only make calls to a trace method of the same name. Revisit |
- // this if our mixin/vtable assumption changes. |
- if (Config::IsTraceMethod(fn) && |
- fn->getName() == trace_->getName() && |
- member->hasQualifier()) { |
- if (const Type* type = member->getQualifier()->getAsType()) { |
- if (CXXRecordDecl* decl = type->getAsCXXRecordDecl()) { |
- RecordInfo::Bases::iterator it = info_->GetBases().find(decl); |
- if (it != info_->GetBases().end()) |
- it->second.MarkTraced(); |
+ // Check for TraceIfNeeded<T>::trace(visitor, &field) |
+ if (call->getNumArgs() == 2 && fn_name == kTraceName && |
+ tmpl->getName() == kTraceIfNeededName) { |
+ FindFieldVisitor finder; |
+ finder.TraverseStmt(call->getArg(1)); |
+ if (finder.field()) |
+ FoundField(finder.field()); |
+ } |
+ } |
+ |
+ bool CheckTraceBaseCall(CallExpr* call) { |
+ MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee()); |
+ if (!callee) |
+ return false; |
+ |
+ FunctionDecl* fn = dyn_cast<FunctionDecl>(callee->getMemberDecl()); |
+ if (!fn || !Config::IsTraceMethod(fn)) |
+ return false; |
+ |
+ // Currently, a manually dispatched class cannot have mixin bases (having |
+ // one would add a vtable which we explicitly check against). This means |
+ // that we can only make calls to a trace method of the same name. Revisit |
+ // this if our mixin/vtable assumption changes. |
+ if (fn->getName() != trace_->getName()) |
+ return false; |
+ |
+ CXXRecordDecl* decl = 0; |
+ if (callee && callee->hasQualifier()) { |
+ if (const Type* type = callee->getQualifier()->getAsType()) |
+ decl = type->getAsCXXRecordDecl(); |
+ } |
+ if (!decl) |
+ return false; |
+ |
+ RecordInfo::Bases::iterator it = info_->GetBases().find(decl); |
+ if (it != info_->GetBases().end()) { |
+ it->second.MarkTraced(); |
+ } |
+ |
+ return true; |
+ } |
+ |
+ bool CheckTraceFieldCall(CXXMemberCallExpr* call) { |
+ return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), |
+ call->getRecordDecl(), |
+ call->getArg(0)); |
+ } |
+ |
+ bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { |
+ if (name != kTraceName || !Config::IsVisitor(callee->getName())) |
+ return false; |
+ |
+ FindFieldVisitor finder; |
+ finder.TraverseStmt(arg); |
+ if (finder.field()) |
+ FoundField(finder.field()); |
+ |
+ return true; |
+ } |
+ |
+ bool CheckRegisterWeakMembers(CXXMemberCallExpr* call) { |
+ CXXMethodDecl* fn = call->getMethodDecl(); |
+ if (fn->getName() != kRegisterWeakMembersName) |
+ return false; |
+ |
+ if (fn->isTemplateInstantiation()) { |
+ const TemplateArgumentList& args = |
+ *fn->getTemplateSpecializationInfo()->TemplateArguments; |
+ // The second template argument is the callback method. |
+ if (args.size() > 1 && |
+ args[1].getKind() == TemplateArgument::Declaration) { |
+ if (FunctionDecl* callback = |
+ dyn_cast<FunctionDecl>(args[1].getAsDecl())) { |
+ if (callback->hasBody()) { |
+ CheckTraceVisitor nested_visitor(info_); |
+ nested_visitor.TraverseStmt(callback->getBody()); |
} |
} |
} |
@@ -378,7 +495,24 @@ class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
return true; |
} |
- private: |
+ class FindFieldVisitor : public RecursiveASTVisitor<FindFieldVisitor> { |
+ public: |
+ FindFieldVisitor() : member_(0), field_(0) {} |
+ MemberExpr* member() const { return member_; } |
+ FieldDecl* field() const { return field_; } |
+ bool TraverseMemberExpr(MemberExpr* member) { |
+ if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { |
+ member_ = member; |
+ field_ = field; |
+ return false; |
+ } |
+ return true; |
+ } |
+ private: |
+ MemberExpr* member_; |
+ FieldDecl* field_; |
+ }; |
+ |
// Nested checking for weak callbacks. |
CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} |
@@ -391,6 +525,27 @@ class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
it->second.MarkTraced(); |
} |
+ void FoundField(FieldDecl* field) { |
+ if (IsTemplateInstantiation(info_->record())) { |
+ // Pointer equality on fields does not work for template instantiations. |
+ // The trace method refers to fields of the template definition which |
+ // are different from the instantiated fields that need to be traced. |
+ const string& name = field->getNameAsString(); |
+ for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); |
+ it != info_->GetFields().end(); |
+ ++it) { |
+ if (it->first->getNameAsString() == name) { |
+ MarkTraced(it); |
+ break; |
+ } |
+ } |
+ } else { |
+ RecordInfo::Fields::iterator it = info_->GetFields().find(field); |
+ if (it != info_->GetFields().end()) |
+ MarkTraced(it); |
+ } |
+ } |
+ |
CXXMethodDecl* trace_; |
RecordInfo* info_; |
}; |
@@ -574,8 +729,7 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
options_(options), |
json_(0) { |
- // Only check structures in the blink, blink and WebKit namespaces. |
- options_.checked_namespaces.insert("blink"); |
+ // Only check structures in the blink and WebKit namespaces. |
options_.checked_namespaces.insert("blink"); |
options_.checked_namespaces.insert("WebKit"); |