| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // This clang plugin checks various invariants of the Blink garbage | 5 // This clang plugin checks various invariants of the Blink garbage |
| 6 // collection infrastructure. | 6 // collection infrastructure. |
| 7 // | 7 // |
| 8 // Errors are described at: | 8 // Errors are described at: |
| 9 // http://www.chromium.org/developers/blink-gc-plugin-errors | 9 // http://www.chromium.org/developers/blink-gc-plugin-errors |
| 10 | 10 |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 | 190 |
| 191 // Collect record declarations, including nested declarations. | 191 // Collect record declarations, including nested declarations. |
| 192 bool VisitCXXRecordDecl(CXXRecordDecl* record) { | 192 bool VisitCXXRecordDecl(CXXRecordDecl* record) { |
| 193 if (record->hasDefinition() && record->isCompleteDefinition()) | 193 if (record->hasDefinition() && record->isCompleteDefinition()) |
| 194 record_decls_.push_back(record); | 194 record_decls_.push_back(record); |
| 195 return true; | 195 return true; |
| 196 } | 196 } |
| 197 | 197 |
| 198 // Collect tracing method definitions, but don't traverse method bodies. | 198 // Collect tracing method definitions, but don't traverse method bodies. |
| 199 bool TraverseCXXMethodDecl(CXXMethodDecl* method) { | 199 bool TraverseCXXMethodDecl(CXXMethodDecl* method) { |
| 200 if (method->isThisDeclarationADefinition() && Config::IsTraceMethod(method)) | 200 if (method->isThisDeclarationADefinition() && |
| 201 Config::IsTraceMethod(method, nullptr)) |
| 201 trace_decls_.push_back(method); | 202 trace_decls_.push_back(method); |
| 202 return true; | 203 return true; |
| 203 } | 204 } |
| 204 | 205 |
| 205 private: | 206 private: |
| 206 RecordVector record_decls_; | 207 RecordVector record_decls_; |
| 207 MethodVector trace_decls_; | 208 MethodVector trace_decls_; |
| 208 }; | 209 }; |
| 209 | 210 |
| 210 // This visitor checks that a finalizer method does not have invalid access to | 211 // This visitor checks that a finalizer method does not have invalid access to |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 RecordInfo* receiver_; | 316 RecordInfo* receiver_; |
| 316 bool dispatched_to_receiver_; | 317 bool dispatched_to_receiver_; |
| 317 }; | 318 }; |
| 318 | 319 |
| 319 // This visitor checks a tracing method by traversing its body. | 320 // This visitor checks a tracing method by traversing its body. |
| 320 // - A member field is considered traced if it is referenced in the body. | 321 // - A member field is considered traced if it is referenced in the body. |
| 321 // - A base is traced if a base-qualified call to a trace method is found. | 322 // - A base is traced if a base-qualified call to a trace method is found. |
| 322 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { | 323 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
| 323 public: | 324 public: |
| 324 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) | 325 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) |
| 325 : trace_(trace), info_(info) {} | 326 : trace_(trace), info_(info), delegates_to_traceimpl_(false) {} |
| 327 |
| 328 bool delegates_to_traceimpl() const { return delegates_to_traceimpl_; } |
| 326 | 329 |
| 327 bool VisitMemberExpr(MemberExpr* member) { | 330 bool VisitMemberExpr(MemberExpr* member) { |
| 328 // In weak callbacks, consider any occurrence as a correct usage. | 331 // In weak callbacks, consider any occurrence as a correct usage. |
| 329 // TODO: We really want to require that isAlive is checked on manually | 332 // TODO: We really want to require that isAlive is checked on manually |
| 330 // processed weak fields. | 333 // processed weak fields. |
| 331 if (IsWeakCallback()) { | 334 if (IsWeakCallback()) { |
| 332 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) | 335 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) |
| 333 FoundField(field); | 336 FoundField(field); |
| 334 } | 337 } |
| 335 return true; | 338 return true; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 376 return true; | 379 return true; |
| 377 CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); | 380 CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); |
| 378 if (decl) | 381 if (decl) |
| 379 CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); | 382 CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); |
| 380 return true; | 383 return true; |
| 381 } | 384 } |
| 382 | 385 |
| 383 if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { | 386 if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { |
| 384 if (CheckTraceFieldCall(expr) || CheckRegisterWeakMembers(expr)) | 387 if (CheckTraceFieldCall(expr) || CheckRegisterWeakMembers(expr)) |
| 385 return true; | 388 return true; |
| 389 |
| 390 if (expr->getMethodDecl()->getNameAsString() == kTraceImplName) { |
| 391 delegates_to_traceimpl_ = true; |
| 392 return true; |
| 393 } |
| 386 } | 394 } |
| 387 | 395 |
| 388 CheckTraceBaseCall(call); | 396 CheckTraceBaseCall(call); |
| 389 return true; | 397 return true; |
| 390 } | 398 } |
| 391 | 399 |
| 392 private: | 400 private: |
| 393 | 401 |
| 394 CXXRecordDecl* GetDependentTemplatedDecl(CXXDependentScopeMemberExpr* expr) { | 402 CXXRecordDecl* GetDependentTemplatedDecl(CXXDependentScopeMemberExpr* expr) { |
| 395 NestedNameSpecifier* qual = expr->getQualifier(); | 403 NestedNameSpecifier* qual = expr->getQualifier(); |
| 396 if (!qual) | 404 if (!qual) |
| 397 return 0; | 405 return 0; |
| 398 | 406 |
| 399 const Type* type = qual->getAsType(); | 407 const Type* type = qual->getAsType(); |
| 400 if (!type) | 408 if (!type) |
| 401 return 0; | 409 return 0; |
| 402 | 410 |
| 403 const TemplateSpecializationType* tmpl_type = | 411 return RecordInfo::GetDependentTemplatedDecl(*type); |
| 404 type->getAs<TemplateSpecializationType>(); | |
| 405 if (!tmpl_type) | |
| 406 return 0; | |
| 407 | |
| 408 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); | |
| 409 if (!tmpl_decl) | |
| 410 return 0; | |
| 411 | |
| 412 return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); | |
| 413 } | 412 } |
| 414 | 413 |
| 415 void CheckCXXDependentScopeMemberExpr(CallExpr* call, | 414 void CheckCXXDependentScopeMemberExpr(CallExpr* call, |
| 416 CXXDependentScopeMemberExpr* expr) { | 415 CXXDependentScopeMemberExpr* expr) { |
| 417 string fn_name = expr->getMember().getAsString(); | 416 string fn_name = expr->getMember().getAsString(); |
| 417 |
| 418 // Check for VisitorDispatcher::trace(field) |
| 419 if (!expr->isImplicitAccess()) { |
| 420 if (clang::DeclRefExpr* base_decl = |
| 421 clang::dyn_cast<clang::DeclRefExpr>(expr->getBase())) { |
| 422 if (Config::IsVisitorDispatcherType(base_decl->getType()) && |
| 423 call->getNumArgs() == 1 && fn_name == kTraceName) { |
| 424 FindFieldVisitor finder; |
| 425 finder.TraverseStmt(call->getArg(0)); |
| 426 if (finder.field()) |
| 427 FoundField(finder.field()); |
| 428 |
| 429 return; |
| 430 } |
| 431 } |
| 432 } |
| 433 |
| 418 CXXRecordDecl* tmpl = GetDependentTemplatedDecl(expr); | 434 CXXRecordDecl* tmpl = GetDependentTemplatedDecl(expr); |
| 419 if (!tmpl) | 435 if (!tmpl) |
| 420 return; | 436 return; |
| 421 | 437 |
| 422 // Check for Super<T>::trace(visitor) | 438 // Check for Super<T>::trace(visitor) |
| 423 if (call->getNumArgs() == 1 && fn_name == trace_->getName()) { | 439 if (call->getNumArgs() == 1 && fn_name == trace_->getName()) { |
| 424 RecordInfo::Bases::iterator it = info_->GetBases().begin(); | 440 RecordInfo::Bases::iterator it = info_->GetBases().begin(); |
| 425 for (; it != info_->GetBases().end(); ++it) { | 441 for (; it != info_->GetBases().end(); ++it) { |
| 426 if (it->first->getName() == tmpl->getName()) | 442 if (it->first->getName() == tmpl->getName()) |
| 427 it->second.MarkTraced(); | 443 it->second.MarkTraced(); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 438 FoundField(finder.field()); | 454 FoundField(finder.field()); |
| 439 } | 455 } |
| 440 } | 456 } |
| 441 | 457 |
| 442 bool CheckTraceBaseCall(CallExpr* call) { | 458 bool CheckTraceBaseCall(CallExpr* call) { |
| 443 MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee()); | 459 MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee()); |
| 444 if (!callee) | 460 if (!callee) |
| 445 return false; | 461 return false; |
| 446 | 462 |
| 447 FunctionDecl* fn = dyn_cast<FunctionDecl>(callee->getMemberDecl()); | 463 FunctionDecl* fn = dyn_cast<FunctionDecl>(callee->getMemberDecl()); |
| 448 if (!fn || !Config::IsTraceMethod(fn)) | 464 if (!fn || !Config::IsTraceMethod(fn, nullptr)) |
| 449 return false; | 465 return false; |
| 450 | 466 |
| 451 // Currently, a manually dispatched class cannot have mixin bases (having | 467 // Currently, a manually dispatched class cannot have mixin bases (having |
| 452 // one would add a vtable which we explicitly check against). This means | 468 // one would add a vtable which we explicitly check against). This means |
| 453 // that we can only make calls to a trace method of the same name. Revisit | 469 // that we can only make calls to a trace method of the same name. Revisit |
| 454 // this if our mixin/vtable assumption changes. | 470 // this if our mixin/vtable assumption changes. |
| 455 if (fn->getName() != trace_->getName()) | 471 if (fn->getName() != trace_->getName()) |
| 456 return false; | 472 return false; |
| 457 | 473 |
| 458 CXXRecordDecl* decl = 0; | 474 CXXRecordDecl* decl = 0; |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 558 } | 574 } |
| 559 } else { | 575 } else { |
| 560 RecordInfo::Fields::iterator it = info_->GetFields().find(field); | 576 RecordInfo::Fields::iterator it = info_->GetFields().find(field); |
| 561 if (it != info_->GetFields().end()) | 577 if (it != info_->GetFields().end()) |
| 562 MarkTraced(it); | 578 MarkTraced(it); |
| 563 } | 579 } |
| 564 } | 580 } |
| 565 | 581 |
| 566 CXXMethodDecl* trace_; | 582 CXXMethodDecl* trace_; |
| 567 RecordInfo* info_; | 583 RecordInfo* info_; |
| 584 bool delegates_to_traceimpl_; |
| 568 }; | 585 }; |
| 569 | 586 |
| 570 // This visitor checks that the fields of a class and the fields of | 587 // This visitor checks that the fields of a class and the fields of |
| 571 // its part objects don't define GC roots. | 588 // its part objects don't define GC roots. |
| 572 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { | 589 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { |
| 573 public: | 590 public: |
| 574 typedef std::vector<FieldPoint*> RootPath; | 591 typedef std::vector<FieldPoint*> RootPath; |
| 575 typedef std::set<RecordInfo*> VisitingSet; | 592 typedef std::set<RecordInfo*> VisitingSet; |
| 576 typedef std::vector<RootPath> Errors; | 593 typedef std::vector<RootPath> Errors; |
| 577 | 594 |
| (...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1035 // ensure that the left-most base defines a vtable. This ensures that the | 1052 // ensure that the left-most base defines a vtable. This ensures that the |
| 1036 // first thing to be initialized when constructing the object is the vtable | 1053 // first thing to be initialized when constructing the object is the vtable |
| 1037 // itself. | 1054 // itself. |
| 1038 void CheckPolymorphicClass(RecordInfo* info, CXXMethodDecl* trace) { | 1055 void CheckPolymorphicClass(RecordInfo* info, CXXMethodDecl* trace) { |
| 1039 CXXRecordDecl* left_most = info->record(); | 1056 CXXRecordDecl* left_most = info->record(); |
| 1040 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | 1057 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
| 1041 CXXRecordDecl* left_most_base = 0; | 1058 CXXRecordDecl* left_most_base = 0; |
| 1042 while (it != left_most->bases_end()) { | 1059 while (it != left_most->bases_end()) { |
| 1043 left_most_base = it->getType()->getAsCXXRecordDecl(); | 1060 left_most_base = it->getType()->getAsCXXRecordDecl(); |
| 1044 if (!left_most_base && it->getType()->isDependentType()) | 1061 if (!left_most_base && it->getType()->isDependentType()) |
| 1045 left_most_base = GetDependentTemplatedDecl(*it->getType()); | 1062 left_most_base = RecordInfo::GetDependentTemplatedDecl(*it->getType()); |
| 1046 | 1063 |
| 1047 // TODO: Find a way to correctly check actual instantiations | 1064 // TODO: Find a way to correctly check actual instantiations |
| 1048 // for dependent types. The escape below will be hit, eg, when | 1065 // for dependent types. The escape below will be hit, eg, when |
| 1049 // we have a primary template with no definition and | 1066 // we have a primary template with no definition and |
| 1050 // specializations for each case (such as SupplementBase) in | 1067 // specializations for each case (such as SupplementBase) in |
| 1051 // which case we don't succeed in checking the required | 1068 // which case we don't succeed in checking the required |
| 1052 // properties. | 1069 // properties. |
| 1053 if (!left_most_base || !left_most_base->hasDefinition()) | 1070 if (!left_most_base || !left_most_base->hasDefinition()) |
| 1054 return; | 1071 return; |
| 1055 | 1072 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1096 } | 1113 } |
| 1097 } | 1114 } |
| 1098 ReportLeftMostBaseMustBePolymorphic(info, left_most); | 1115 ReportLeftMostBaseMustBePolymorphic(info, left_most); |
| 1099 } | 1116 } |
| 1100 } | 1117 } |
| 1101 | 1118 |
| 1102 CXXRecordDecl* GetLeftMostBase(CXXRecordDecl* left_most) { | 1119 CXXRecordDecl* GetLeftMostBase(CXXRecordDecl* left_most) { |
| 1103 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | 1120 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
| 1104 while (it != left_most->bases_end()) { | 1121 while (it != left_most->bases_end()) { |
| 1105 if (it->getType()->isDependentType()) | 1122 if (it->getType()->isDependentType()) |
| 1106 left_most = GetDependentTemplatedDecl(*it->getType()); | 1123 left_most = RecordInfo::GetDependentTemplatedDecl(*it->getType()); |
| 1107 else | 1124 else |
| 1108 left_most = it->getType()->getAsCXXRecordDecl(); | 1125 left_most = it->getType()->getAsCXXRecordDecl(); |
| 1109 if (!left_most || !left_most->hasDefinition()) | 1126 if (!left_most || !left_most->hasDefinition()) |
| 1110 return 0; | 1127 return 0; |
| 1111 it = left_most->bases_begin(); | 1128 it = left_most->bases_begin(); |
| 1112 } | 1129 } |
| 1113 return left_most; | 1130 return left_most; |
| 1114 } | 1131 } |
| 1115 | 1132 |
| 1116 bool DeclaresVirtualMethods(CXXRecordDecl* decl) { | 1133 bool DeclaresVirtualMethods(CXXRecordDecl* decl) { |
| 1117 CXXRecordDecl::method_iterator it = decl->method_begin(); | 1134 CXXRecordDecl::method_iterator it = decl->method_begin(); |
| 1118 for (; it != decl->method_end(); ++it) | 1135 for (; it != decl->method_end(); ++it) |
| 1119 if (it->isVirtual() && !it->isPure()) | 1136 if (it->isVirtual() && !it->isPure()) |
| 1120 return true; | 1137 return true; |
| 1121 return false; | 1138 return false; |
| 1122 } | 1139 } |
| 1123 | 1140 |
| 1124 void CheckLeftMostDerived(RecordInfo* info) { | 1141 void CheckLeftMostDerived(RecordInfo* info) { |
| 1125 CXXRecordDecl* left_most = info->record(); | 1142 CXXRecordDecl* left_most = GetLeftMostBase(info->record()); |
| 1126 CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); | 1143 if (!left_most) |
| 1127 while (it != left_most->bases_end()) { | 1144 return; |
| 1128 left_most = it->getType()->getAsCXXRecordDecl(); | |
| 1129 it = left_most->bases_begin(); | |
| 1130 } | |
| 1131 if (!Config::IsGCBase(left_most->getName())) | 1145 if (!Config::IsGCBase(left_most->getName())) |
| 1132 ReportClassMustLeftMostlyDeriveGC(info); | 1146 ReportClassMustLeftMostlyDeriveGC(info); |
| 1133 } | 1147 } |
| 1134 | 1148 |
| 1135 void CheckDispatch(RecordInfo* info) { | 1149 void CheckDispatch(RecordInfo* info) { |
| 1136 bool finalized = info->IsGCFinalized(); | 1150 bool finalized = info->IsGCFinalized(); |
| 1137 CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod(); | 1151 CXXMethodDecl* trace_dispatch = info->GetTraceDispatchMethod(); |
| 1138 CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod(); | 1152 CXXMethodDecl* finalize_dispatch = info->GetFinalizeDispatchMethod(); |
| 1139 if (!trace_dispatch && !finalize_dispatch) | 1153 if (!trace_dispatch && !finalize_dispatch) |
| 1140 return; | 1154 return; |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1296 ++it) { | 1310 ++it) { |
| 1297 RecordInfo* base = it->second.info(); | 1311 RecordInfo* base = it->second.info(); |
| 1298 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) | 1312 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
| 1299 ReportOverriddenNonVirtualTrace(parent, trace, other); | 1313 ReportOverriddenNonVirtualTrace(parent, trace, other); |
| 1300 } | 1314 } |
| 1301 } | 1315 } |
| 1302 | 1316 |
| 1303 CheckTraceVisitor visitor(trace, parent); | 1317 CheckTraceVisitor visitor(trace, parent); |
| 1304 visitor.TraverseCXXMethodDecl(trace); | 1318 visitor.TraverseCXXMethodDecl(trace); |
| 1305 | 1319 |
| 1320 // Skip reporting if this trace method is a just delegate to |
| 1321 // traceImpl method. We will report on CheckTraceMethod on traceImpl method. |
| 1322 if (visitor.delegates_to_traceimpl()) |
| 1323 return; |
| 1324 |
| 1306 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1325 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
| 1307 it != parent->GetBases().end(); | 1326 it != parent->GetBases().end(); |
| 1308 ++it) { | 1327 ++it) { |
| 1309 if (!it->second.IsProperlyTraced()) | 1328 if (!it->second.IsProperlyTraced()) |
| 1310 ReportBaseRequiresTracing(parent, trace, it->first); | 1329 ReportBaseRequiresTracing(parent, trace, it->first); |
| 1311 } | 1330 } |
| 1312 | 1331 |
| 1313 for (RecordInfo::Fields::iterator it = parent->GetFields().begin(); | 1332 for (RecordInfo::Fields::iterator it = parent->GetFields().begin(); |
| 1314 it != parent->GetFields().end(); | 1333 it != parent->GetFields().end(); |
| 1315 ++it) { | 1334 ++it) { |
| (...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1887 | 1906 |
| 1888 private: | 1907 private: |
| 1889 BlinkGCPluginOptions options_; | 1908 BlinkGCPluginOptions options_; |
| 1890 }; | 1909 }; |
| 1891 | 1910 |
| 1892 } // namespace | 1911 } // namespace |
| 1893 | 1912 |
| 1894 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 1913 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( |
| 1895 "blink-gc-plugin", | 1914 "blink-gc-plugin", |
| 1896 "Check Blink GC invariants"); | 1915 "Check Blink GC invariants"); |
| OLD | NEW |