OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "CheckTraceVisitor.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "Config.h" |
| 10 |
| 11 using namespace clang; |
| 12 |
| 13 CheckTraceVisitor::CheckTraceVisitor(CXXMethodDecl* trace, |
| 14 RecordInfo* info, |
| 15 RecordCache* cache) |
| 16 : trace_(trace), |
| 17 info_(info), |
| 18 cache_(cache), |
| 19 delegates_to_traceimpl_(false) { |
| 20 } |
| 21 |
| 22 bool CheckTraceVisitor::delegates_to_traceimpl() const { |
| 23 return delegates_to_traceimpl_; |
| 24 } |
| 25 |
| 26 bool CheckTraceVisitor::VisitMemberExpr(MemberExpr* member) { |
| 27 // In weak callbacks, consider any occurrence as a correct usage. |
| 28 // TODO: We really want to require that isAlive is checked on manually |
| 29 // processed weak fields. |
| 30 if (IsWeakCallback()) { |
| 31 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) |
| 32 FoundField(field); |
| 33 } |
| 34 return true; |
| 35 } |
| 36 |
| 37 bool CheckTraceVisitor::VisitCallExpr(CallExpr* call) { |
| 38 // In weak callbacks we don't check calls (see VisitMemberExpr). |
| 39 if (IsWeakCallback()) |
| 40 return true; |
| 41 |
| 42 Expr* callee = call->getCallee(); |
| 43 |
| 44 // Trace calls from a templated derived class result in a |
| 45 // DependentScopeMemberExpr because the concrete trace call depends on the |
| 46 // instantiation of any shared template parameters. In this case the call is |
| 47 // "unresolved" and we resort to comparing the syntactic type names. |
| 48 if (CXXDependentScopeMemberExpr* expr = |
| 49 dyn_cast<CXXDependentScopeMemberExpr>(callee)) { |
| 50 CheckCXXDependentScopeMemberExpr(call, expr); |
| 51 return true; |
| 52 } |
| 53 |
| 54 // A tracing call will have either a |visitor| or a |m_field| argument. |
| 55 // A registerWeakMembers call will have a |this| argument. |
| 56 if (call->getNumArgs() != 1) |
| 57 return true; |
| 58 Expr* arg = call->getArg(0); |
| 59 |
| 60 if (UnresolvedMemberExpr* expr = dyn_cast<UnresolvedMemberExpr>(callee)) { |
| 61 // This could be a trace call of a base class, as explained in the |
| 62 // comments of CheckTraceBaseCall(). |
| 63 if (CheckTraceBaseCall(call)) |
| 64 return true; |
| 65 |
| 66 if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) |
| 67 MarkAllWeakMembersTraced(); |
| 68 |
| 69 QualType base = expr->getBaseType(); |
| 70 if (!base->isPointerType()) |
| 71 return true; |
| 72 CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); |
| 73 if (decl) |
| 74 CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); |
| 75 if (Config::IsTraceImplName(expr->getMemberName().getAsString())) |
| 76 delegates_to_traceimpl_ = true; |
| 77 return true; |
| 78 } |
| 79 |
| 80 if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { |
| 81 if (CheckTraceFieldMemberCall(expr) || CheckRegisterWeakMembers(expr)) |
| 82 return true; |
| 83 |
| 84 if (Config::IsTraceImplName(expr->getMethodDecl()->getNameAsString())) { |
| 85 delegates_to_traceimpl_ = true; |
| 86 return true; |
| 87 } |
| 88 } |
| 89 |
| 90 CheckTraceBaseCall(call); |
| 91 return true; |
| 92 } |
| 93 |
| 94 bool CheckTraceVisitor::IsTraceCallName(const std::string& name) { |
| 95 if (trace_->getName() == kTraceImplName) |
| 96 return name == kTraceName; |
| 97 if (trace_->getName() == kTraceAfterDispatchImplName) |
| 98 return name == kTraceAfterDispatchName; |
| 99 // Currently, a manually dispatched class cannot have mixin bases (having |
| 100 // one would add a vtable which we explicitly check against). This means |
| 101 // that we can only make calls to a trace method of the same name. Revisit |
| 102 // this if our mixin/vtable assumption changes. |
| 103 return name == trace_->getName(); |
| 104 } |
| 105 |
| 106 CXXRecordDecl* CheckTraceVisitor::GetDependentTemplatedDecl( |
| 107 CXXDependentScopeMemberExpr* expr) { |
| 108 NestedNameSpecifier* qual = expr->getQualifier(); |
| 109 if (!qual) |
| 110 return 0; |
| 111 |
| 112 const Type* type = qual->getAsType(); |
| 113 if (!type) |
| 114 return 0; |
| 115 |
| 116 return RecordInfo::GetDependentTemplatedDecl(*type); |
| 117 } |
| 118 |
| 119 namespace { |
| 120 |
| 121 class FindFieldVisitor : public RecursiveASTVisitor<FindFieldVisitor> { |
| 122 public: |
| 123 FindFieldVisitor(); |
| 124 MemberExpr* member() const; |
| 125 FieldDecl* field() const; |
| 126 bool TraverseMemberExpr(MemberExpr* member); |
| 127 |
| 128 private: |
| 129 MemberExpr* member_; |
| 130 FieldDecl* field_; |
| 131 }; |
| 132 |
| 133 FindFieldVisitor::FindFieldVisitor() |
| 134 : member_(0), |
| 135 field_(0) { |
| 136 } |
| 137 |
| 138 MemberExpr* FindFieldVisitor::member() const { |
| 139 return member_; |
| 140 } |
| 141 |
| 142 FieldDecl* FindFieldVisitor::field() const { |
| 143 return field_; |
| 144 } |
| 145 |
| 146 bool FindFieldVisitor::TraverseMemberExpr(MemberExpr* member) { |
| 147 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { |
| 148 member_ = member; |
| 149 field_ = field; |
| 150 return false; |
| 151 } |
| 152 return true; |
| 153 } |
| 154 |
| 155 } // namespace |
| 156 |
| 157 void CheckTraceVisitor::CheckCXXDependentScopeMemberExpr( |
| 158 CallExpr* call, |
| 159 CXXDependentScopeMemberExpr* expr) { |
| 160 std::string fn_name = expr->getMember().getAsString(); |
| 161 |
| 162 // Check for VisitorDispatcher::trace(field) and |
| 163 // VisitorDispatcher::registerWeakMembers. |
| 164 if (!expr->isImplicitAccess()) { |
| 165 if (DeclRefExpr* base_decl = dyn_cast<DeclRefExpr>(expr->getBase())) { |
| 166 if (Config::IsVisitorDispatcherType(base_decl->getType())) { |
| 167 if (call->getNumArgs() == 1 && fn_name == kTraceName) { |
| 168 FindFieldVisitor finder; |
| 169 finder.TraverseStmt(call->getArg(0)); |
| 170 if (finder.field()) |
| 171 FoundField(finder.field()); |
| 172 |
| 173 return; |
| 174 } else if (call->getNumArgs() == 1 && |
| 175 fn_name == kRegisterWeakMembersName) { |
| 176 MarkAllWeakMembersTraced(); |
| 177 } |
| 178 } |
| 179 } |
| 180 } |
| 181 |
| 182 CXXRecordDecl* tmpl = GetDependentTemplatedDecl(expr); |
| 183 if (!tmpl) |
| 184 return; |
| 185 |
| 186 // Check for Super<T>::trace(visitor) |
| 187 if (call->getNumArgs() == 1 && IsTraceCallName(fn_name)) { |
| 188 RecordInfo::Bases::iterator it = info_->GetBases().begin(); |
| 189 for (; it != info_->GetBases().end(); ++it) { |
| 190 if (it->first->getName() == tmpl->getName()) |
| 191 it->second.MarkTraced(); |
| 192 } |
| 193 } |
| 194 |
| 195 // Check for TraceIfNeeded<T>::trace(visitor, &field) |
| 196 if (call->getNumArgs() == 2 && fn_name == kTraceName && |
| 197 tmpl->getName() == kTraceIfNeededName) { |
| 198 FindFieldVisitor finder; |
| 199 finder.TraverseStmt(call->getArg(1)); |
| 200 if (finder.field()) |
| 201 FoundField(finder.field()); |
| 202 } |
| 203 } |
| 204 |
| 205 bool CheckTraceVisitor::CheckTraceBaseCall(CallExpr* call) { |
| 206 // Checks for "Base::trace(visitor)"-like calls. |
| 207 |
| 208 // Checking code for these two variables is shared among MemberExpr* case |
| 209 // and UnresolvedMemberCase* case below. |
| 210 // |
| 211 // For example, if we've got "Base::trace(visitor)" as |call|, |
| 212 // callee_record will be "Base", and func_name will be "trace". |
| 213 CXXRecordDecl* callee_record = nullptr; |
| 214 std::string func_name; |
| 215 |
| 216 if (MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee())) { |
| 217 if (!callee->hasQualifier()) |
| 218 return false; |
| 219 |
| 220 FunctionDecl* trace_decl = |
| 221 dyn_cast<FunctionDecl>(callee->getMemberDecl()); |
| 222 if (!trace_decl || !Config::IsTraceMethod(trace_decl)) |
| 223 return false; |
| 224 |
| 225 const Type* type = callee->getQualifier()->getAsType(); |
| 226 if (!type) |
| 227 return false; |
| 228 |
| 229 callee_record = type->getAsCXXRecordDecl(); |
| 230 func_name = trace_decl->getName(); |
| 231 } else if (UnresolvedMemberExpr* callee = |
| 232 dyn_cast<UnresolvedMemberExpr>(call->getCallee())) { |
| 233 // Callee part may become unresolved if the type of the argument |
| 234 // ("visitor") is a template parameter and the called function is |
| 235 // overloaded (i.e. trace(Visitor*) and |
| 236 // trace(InlinedGlobalMarkingVisitor)). |
| 237 // |
| 238 // Here, we try to find a function that looks like trace() from the |
| 239 // candidate overloaded functions, and if we find one, we assume it is |
| 240 // called here. |
| 241 |
| 242 CXXMethodDecl* trace_decl = nullptr; |
| 243 for (NamedDecl* named_decl : callee->decls()) { |
| 244 if (CXXMethodDecl* method_decl = dyn_cast<CXXMethodDecl>(named_decl)) { |
| 245 if (Config::IsTraceMethod(method_decl)) { |
| 246 trace_decl = method_decl; |
| 247 break; |
| 248 } |
| 249 } |
| 250 } |
| 251 if (!trace_decl) |
| 252 return false; |
| 253 |
| 254 // Check if the passed argument is named "visitor". |
| 255 if (call->getNumArgs() != 1) |
| 256 return false; |
| 257 DeclRefExpr* arg = dyn_cast<DeclRefExpr>(call->getArg(0)); |
| 258 if (!arg || arg->getNameInfo().getAsString() != kVisitorVarName) |
| 259 return false; |
| 260 |
| 261 callee_record = trace_decl->getParent(); |
| 262 func_name = callee->getMemberName().getAsString(); |
| 263 } |
| 264 |
| 265 if (!callee_record) |
| 266 return false; |
| 267 |
| 268 if (!IsTraceCallName(func_name)) |
| 269 return false; |
| 270 |
| 271 for (auto& base : info_->GetBases()) { |
| 272 // We want to deal with omitted trace() function in an intermediary |
| 273 // class in the class hierarchy, e.g.: |
| 274 // class A : public GarbageCollected<A> { trace() { ... } }; |
| 275 // class B : public A { /* No trace(); have nothing to trace. */ }; |
| 276 // class C : public B { trace() { B::trace(visitor); } } |
| 277 // where, B::trace() is actually A::trace(), and in some cases we get |
| 278 // A as |callee_record| instead of B. We somehow need to mark B as |
| 279 // traced if we find A::trace() call. |
| 280 // |
| 281 // To solve this, here we keep going up the class hierarchy as long as |
| 282 // they are not required to have a trace method. The implementation is |
| 283 // a simple DFS, where |base_records| represents the set of base classes |
| 284 // we need to visit. |
| 285 |
| 286 std::vector<CXXRecordDecl*> base_records; |
| 287 base_records.push_back(base.first); |
| 288 |
| 289 while (!base_records.empty()) { |
| 290 CXXRecordDecl* base_record = base_records.back(); |
| 291 base_records.pop_back(); |
| 292 |
| 293 if (base_record == callee_record) { |
| 294 // If we find a matching trace method, pretend the user has written |
| 295 // a correct trace() method of the base; in the example above, we |
| 296 // find A::trace() here and mark B as correctly traced. |
| 297 base.second.MarkTraced(); |
| 298 return true; |
| 299 } |
| 300 |
| 301 if (RecordInfo* base_info = cache_->Lookup(base_record)) { |
| 302 if (!base_info->RequiresTraceMethod()) { |
| 303 // If this base class is not required to have a trace method, then |
| 304 // the actual trace method may be defined in an ancestor. |
| 305 for (auto& inner_base : base_info->GetBases()) |
| 306 base_records.push_back(inner_base.first); |
| 307 } |
| 308 } |
| 309 } |
| 310 } |
| 311 |
| 312 return false; |
| 313 } |
| 314 |
| 315 bool CheckTraceVisitor::CheckTraceFieldMemberCall(CXXMemberCallExpr* call) { |
| 316 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), |
| 317 call->getRecordDecl(), |
| 318 call->getArg(0)); |
| 319 } |
| 320 |
| 321 bool CheckTraceVisitor::CheckTraceFieldCall( |
| 322 const std::string& name, |
| 323 CXXRecordDecl* callee, |
| 324 Expr* arg) { |
| 325 if (name != kTraceName || !Config::IsVisitor(callee->getName())) |
| 326 return false; |
| 327 |
| 328 FindFieldVisitor finder; |
| 329 finder.TraverseStmt(arg); |
| 330 if (finder.field()) |
| 331 FoundField(finder.field()); |
| 332 |
| 333 return true; |
| 334 } |
| 335 |
| 336 bool CheckTraceVisitor::CheckRegisterWeakMembers(CXXMemberCallExpr* call) { |
| 337 CXXMethodDecl* fn = call->getMethodDecl(); |
| 338 if (fn->getName() != kRegisterWeakMembersName) |
| 339 return false; |
| 340 |
| 341 if (fn->isTemplateInstantiation()) { |
| 342 const TemplateArgumentList& args = |
| 343 *fn->getTemplateSpecializationInfo()->TemplateArguments; |
| 344 // The second template argument is the callback method. |
| 345 if (args.size() > 1 && |
| 346 args[1].getKind() == TemplateArgument::Declaration) { |
| 347 if (FunctionDecl* callback = |
| 348 dyn_cast<FunctionDecl>(args[1].getAsDecl())) { |
| 349 if (callback->hasBody()) { |
| 350 CheckTraceVisitor nested_visitor(nullptr, info_, nullptr); |
| 351 nested_visitor.TraverseStmt(callback->getBody()); |
| 352 } |
| 353 } |
| 354 } |
| 355 } |
| 356 return true; |
| 357 } |
| 358 |
| 359 bool CheckTraceVisitor::IsWeakCallback() const { |
| 360 return !trace_; |
| 361 } |
| 362 |
| 363 void CheckTraceVisitor::MarkTraced(RecordInfo::Fields::iterator it) { |
| 364 // In a weak callback we can't mark strong fields as traced. |
| 365 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) |
| 366 return; |
| 367 it->second.MarkTraced(); |
| 368 } |
| 369 |
| 370 void CheckTraceVisitor::FoundField(FieldDecl* field) { |
| 371 if (Config::IsTemplateInstantiation(info_->record())) { |
| 372 // Pointer equality on fields does not work for template instantiations. |
| 373 // The trace method refers to fields of the template definition which |
| 374 // are different from the instantiated fields that need to be traced. |
| 375 const std::string& name = field->getNameAsString(); |
| 376 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); |
| 377 it != info_->GetFields().end(); |
| 378 ++it) { |
| 379 if (it->first->getNameAsString() == name) { |
| 380 MarkTraced(it); |
| 381 break; |
| 382 } |
| 383 } |
| 384 } else { |
| 385 RecordInfo::Fields::iterator it = info_->GetFields().find(field); |
| 386 if (it != info_->GetFields().end()) |
| 387 MarkTraced(it); |
| 388 } |
| 389 } |
| 390 |
| 391 void CheckTraceVisitor::MarkAllWeakMembersTraced() { |
| 392 // If we find a call to registerWeakMembers which is unresolved we |
| 393 // unsoundly consider all weak members as traced. |
| 394 // TODO: Find out how to validate weak member tracing for unresolved call. |
| 395 for (auto& field : info_->GetFields()) { |
| 396 if (field.second.edge()->IsWeakMember()) |
| 397 field.second.MarkTraced(); |
| 398 } |
| 399 } |
OLD | NEW |