Index: tools/clang/blink_gc_plugin/CheckTraceVisitor.cpp |
diff --git a/tools/clang/blink_gc_plugin/CheckTraceVisitor.cpp b/tools/clang/blink_gc_plugin/CheckTraceVisitor.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c996ca7dd1480a76c65ddf12f3fd8d2215aa7009 |
--- /dev/null |
+++ b/tools/clang/blink_gc_plugin/CheckTraceVisitor.cpp |
@@ -0,0 +1,399 @@ |
+// 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 "CheckTraceVisitor.h" |
+ |
+#include <vector> |
+ |
+#include "Config.h" |
+ |
+using namespace clang; |
+ |
+CheckTraceVisitor::CheckTraceVisitor(CXXMethodDecl* trace, |
+ RecordInfo* info, |
+ RecordCache* cache) |
+ : trace_(trace), |
+ info_(info), |
+ cache_(cache), |
+ delegates_to_traceimpl_(false) { |
+} |
+ |
+bool CheckTraceVisitor::delegates_to_traceimpl() const { |
+ return delegates_to_traceimpl_; |
+} |
+ |
+bool CheckTraceVisitor::VisitMemberExpr(MemberExpr* member) { |
+ // 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 CheckTraceVisitor::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)) { |
+ // This could be a trace call of a base class, as explained in the |
+ // comments of CheckTraceBaseCall(). |
+ if (CheckTraceBaseCall(call)) |
+ return true; |
+ |
+ if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) |
+ MarkAllWeakMembersTraced(); |
+ |
+ QualType base = expr->getBaseType(); |
+ if (!base->isPointerType()) |
+ return true; |
+ CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); |
+ if (decl) |
+ CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); |
+ if (Config::IsTraceImplName(expr->getMemberName().getAsString())) |
+ delegates_to_traceimpl_ = true; |
+ return true; |
+ } |
+ |
+ if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { |
+ if (CheckTraceFieldMemberCall(expr) || CheckRegisterWeakMembers(expr)) |
+ return true; |
+ |
+ if (Config::IsTraceImplName(expr->getMethodDecl()->getNameAsString())) { |
+ delegates_to_traceimpl_ = true; |
+ return true; |
+ } |
+ } |
+ |
+ CheckTraceBaseCall(call); |
+ return true; |
+} |
+ |
+bool CheckTraceVisitor::IsTraceCallName(const std::string& name) { |
+ if (trace_->getName() == kTraceImplName) |
+ return name == kTraceName; |
+ if (trace_->getName() == kTraceAfterDispatchImplName) |
+ return name == kTraceAfterDispatchName; |
+ // 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. |
+ return name == trace_->getName(); |
+} |
+ |
+CXXRecordDecl* CheckTraceVisitor::GetDependentTemplatedDecl( |
+ CXXDependentScopeMemberExpr* expr) { |
+ NestedNameSpecifier* qual = expr->getQualifier(); |
+ if (!qual) |
+ return 0; |
+ |
+ const Type* type = qual->getAsType(); |
+ if (!type) |
+ return 0; |
+ |
+ return RecordInfo::GetDependentTemplatedDecl(*type); |
+} |
+ |
+namespace { |
+ |
+class FindFieldVisitor : public RecursiveASTVisitor<FindFieldVisitor> { |
+ public: |
+ FindFieldVisitor(); |
+ MemberExpr* member() const; |
+ FieldDecl* field() const; |
+ bool TraverseMemberExpr(MemberExpr* member); |
+ |
+ private: |
+ MemberExpr* member_; |
+ FieldDecl* field_; |
+}; |
+ |
+FindFieldVisitor::FindFieldVisitor() |
+ : member_(0), |
+ field_(0) { |
+} |
+ |
+MemberExpr* FindFieldVisitor::member() const { |
+ return member_; |
+} |
+ |
+FieldDecl* FindFieldVisitor::field() const { |
+ return field_; |
+} |
+ |
+bool FindFieldVisitor::TraverseMemberExpr(MemberExpr* member) { |
+ if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { |
+ member_ = member; |
+ field_ = field; |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+void CheckTraceVisitor::CheckCXXDependentScopeMemberExpr( |
+ CallExpr* call, |
+ CXXDependentScopeMemberExpr* expr) { |
+ std::string fn_name = expr->getMember().getAsString(); |
+ |
+ // Check for VisitorDispatcher::trace(field) and |
+ // VisitorDispatcher::registerWeakMembers. |
+ if (!expr->isImplicitAccess()) { |
+ if (DeclRefExpr* base_decl = dyn_cast<DeclRefExpr>(expr->getBase())) { |
+ if (Config::IsVisitorDispatcherType(base_decl->getType())) { |
+ if (call->getNumArgs() == 1 && fn_name == kTraceName) { |
+ FindFieldVisitor finder; |
+ finder.TraverseStmt(call->getArg(0)); |
+ if (finder.field()) |
+ FoundField(finder.field()); |
+ |
+ return; |
+ } else if (call->getNumArgs() == 1 && |
+ fn_name == kRegisterWeakMembersName) { |
+ MarkAllWeakMembersTraced(); |
+ } |
+ } |
+ } |
+ } |
+ |
+ CXXRecordDecl* tmpl = GetDependentTemplatedDecl(expr); |
+ if (!tmpl) |
+ return; |
+ |
+ // Check for Super<T>::trace(visitor) |
+ if (call->getNumArgs() == 1 && IsTraceCallName(fn_name)) { |
+ RecordInfo::Bases::iterator it = info_->GetBases().begin(); |
+ for (; it != info_->GetBases().end(); ++it) { |
+ if (it->first->getName() == tmpl->getName()) |
+ 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 CheckTraceVisitor::CheckTraceBaseCall(CallExpr* call) { |
+ // Checks for "Base::trace(visitor)"-like calls. |
+ |
+ // Checking code for these two variables is shared among MemberExpr* case |
+ // and UnresolvedMemberCase* case below. |
+ // |
+ // For example, if we've got "Base::trace(visitor)" as |call|, |
+ // callee_record will be "Base", and func_name will be "trace". |
+ CXXRecordDecl* callee_record = nullptr; |
+ std::string func_name; |
+ |
+ if (MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee())) { |
+ if (!callee->hasQualifier()) |
+ return false; |
+ |
+ FunctionDecl* trace_decl = |
+ dyn_cast<FunctionDecl>(callee->getMemberDecl()); |
+ if (!trace_decl || !Config::IsTraceMethod(trace_decl)) |
+ return false; |
+ |
+ const Type* type = callee->getQualifier()->getAsType(); |
+ if (!type) |
+ return false; |
+ |
+ callee_record = type->getAsCXXRecordDecl(); |
+ func_name = trace_decl->getName(); |
+ } else if (UnresolvedMemberExpr* callee = |
+ dyn_cast<UnresolvedMemberExpr>(call->getCallee())) { |
+ // Callee part may become unresolved if the type of the argument |
+ // ("visitor") is a template parameter and the called function is |
+ // overloaded (i.e. trace(Visitor*) and |
+ // trace(InlinedGlobalMarkingVisitor)). |
+ // |
+ // Here, we try to find a function that looks like trace() from the |
+ // candidate overloaded functions, and if we find one, we assume it is |
+ // called here. |
+ |
+ CXXMethodDecl* trace_decl = nullptr; |
+ for (NamedDecl* named_decl : callee->decls()) { |
+ if (CXXMethodDecl* method_decl = dyn_cast<CXXMethodDecl>(named_decl)) { |
+ if (Config::IsTraceMethod(method_decl)) { |
+ trace_decl = method_decl; |
+ break; |
+ } |
+ } |
+ } |
+ if (!trace_decl) |
+ return false; |
+ |
+ // Check if the passed argument is named "visitor". |
+ if (call->getNumArgs() != 1) |
+ return false; |
+ DeclRefExpr* arg = dyn_cast<DeclRefExpr>(call->getArg(0)); |
+ if (!arg || arg->getNameInfo().getAsString() != kVisitorVarName) |
+ return false; |
+ |
+ callee_record = trace_decl->getParent(); |
+ func_name = callee->getMemberName().getAsString(); |
+ } |
+ |
+ if (!callee_record) |
+ return false; |
+ |
+ if (!IsTraceCallName(func_name)) |
+ return false; |
+ |
+ for (auto& base : info_->GetBases()) { |
+ // We want to deal with omitted trace() function in an intermediary |
+ // class in the class hierarchy, e.g.: |
+ // class A : public GarbageCollected<A> { trace() { ... } }; |
+ // class B : public A { /* No trace(); have nothing to trace. */ }; |
+ // class C : public B { trace() { B::trace(visitor); } } |
+ // where, B::trace() is actually A::trace(), and in some cases we get |
+ // A as |callee_record| instead of B. We somehow need to mark B as |
+ // traced if we find A::trace() call. |
+ // |
+ // To solve this, here we keep going up the class hierarchy as long as |
+ // they are not required to have a trace method. The implementation is |
+ // a simple DFS, where |base_records| represents the set of base classes |
+ // we need to visit. |
+ |
+ std::vector<CXXRecordDecl*> base_records; |
+ base_records.push_back(base.first); |
+ |
+ while (!base_records.empty()) { |
+ CXXRecordDecl* base_record = base_records.back(); |
+ base_records.pop_back(); |
+ |
+ if (base_record == callee_record) { |
+ // If we find a matching trace method, pretend the user has written |
+ // a correct trace() method of the base; in the example above, we |
+ // find A::trace() here and mark B as correctly traced. |
+ base.second.MarkTraced(); |
+ return true; |
+ } |
+ |
+ if (RecordInfo* base_info = cache_->Lookup(base_record)) { |
+ if (!base_info->RequiresTraceMethod()) { |
+ // If this base class is not required to have a trace method, then |
+ // the actual trace method may be defined in an ancestor. |
+ for (auto& inner_base : base_info->GetBases()) |
+ base_records.push_back(inner_base.first); |
+ } |
+ } |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+bool CheckTraceVisitor::CheckTraceFieldMemberCall(CXXMemberCallExpr* call) { |
+ return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), |
+ call->getRecordDecl(), |
+ call->getArg(0)); |
+} |
+ |
+bool CheckTraceVisitor::CheckTraceFieldCall( |
+ const std::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 CheckTraceVisitor::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(nullptr, info_, nullptr); |
+ nested_visitor.TraverseStmt(callback->getBody()); |
+ } |
+ } |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool CheckTraceVisitor::IsWeakCallback() const { |
+ return !trace_; |
+} |
+ |
+void CheckTraceVisitor::MarkTraced(RecordInfo::Fields::iterator it) { |
+ // In a weak callback we can't mark strong fields as traced. |
+ if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) |
+ return; |
+ it->second.MarkTraced(); |
+} |
+ |
+void CheckTraceVisitor::FoundField(FieldDecl* field) { |
+ if (Config::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 std::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); |
+ } |
+} |
+ |
+void CheckTraceVisitor::MarkAllWeakMembersTraced() { |
+ // 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. |
+ for (auto& field : info_->GetFields()) { |
+ if (field.second.edge()->IsWeakMember()) |
+ field.second.MarkTraced(); |
+ } |
+} |