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 } 400