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 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
300 }; | 300 }; |
301 | 301 |
302 // This visitor checks a tracing method by traversing its body. | 302 // This visitor checks a tracing method by traversing its body. |
303 // - A member field is considered traced if it is referenced in the body. | 303 // - A member field is considered traced if it is referenced in the body. |
304 // - A base is traced if a base-qualified call to a trace method is found. | 304 // - A base is traced if a base-qualified call to a trace method is found. |
305 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { | 305 class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
306 public: | 306 public: |
307 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) | 307 CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) |
308 : trace_(trace), info_(info) {} | 308 : trace_(trace), info_(info) {} |
309 | 309 |
310 // Allow recursive traversal by using VisitMemberExpr. | |
311 bool VisitMemberExpr(MemberExpr* member) { | 310 bool VisitMemberExpr(MemberExpr* member) { |
312 // If this member expression references a field decl, mark it as traced. | 311 // In weak callbacks, consider any occurrence as a correct usage. |
313 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { | 312 // TODO: We really want to require that isAlive is checked on manually |
314 if (IsTemplateInstantiation(info_->record())) { | 313 // processed weak fields. |
315 // Pointer equality on fields does not work for template instantiations. | 314 if (IsWeakCallback()) { |
316 // The trace method refers to fields of the template definition which | 315 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) |
317 // are different from the instantiated fields that need to be traced. | 316 FoundField(field); |
318 const string& name = field->getNameAsString(); | 317 } |
| 318 return true; |
| 319 } |
| 320 |
| 321 bool VisitCallExpr(CallExpr* call) { |
| 322 // In weak callbacks we don't check calls (see VisitMemberExpr). |
| 323 if (IsWeakCallback()) |
| 324 return true; |
| 325 |
| 326 Expr* callee = call->getCallee(); |
| 327 |
| 328 // Trace calls from a templated derived class result in a |
| 329 // DependentScopeMemberExpr because the concrete trace call depends on the |
| 330 // instantiation of any shared template parameters. In this case the call is |
| 331 // "unresolved" and we resort to comparing the syntactic type names. |
| 332 if (CXXDependentScopeMemberExpr* expr = |
| 333 dyn_cast<CXXDependentScopeMemberExpr>(callee)) { |
| 334 CheckCXXDependentScopeMemberExpr(call, expr); |
| 335 return true; |
| 336 } |
| 337 |
| 338 // A tracing call will have either a |visitor| or a |m_field| argument. |
| 339 // A registerWeakMembers call will have a |this| argument. |
| 340 if (call->getNumArgs() != 1) |
| 341 return true; |
| 342 Expr* arg = call->getArg(0); |
| 343 |
| 344 if (UnresolvedMemberExpr* expr = dyn_cast<UnresolvedMemberExpr>(callee)) { |
| 345 // If we find a call to registerWeakMembers which is unresolved we |
| 346 // unsoundly consider all weak members as traced. |
| 347 // TODO: Find out how to validate weak member tracing for unresolved call. |
| 348 if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) { |
319 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); | 349 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); |
320 it != info_->GetFields().end(); | 350 it != info_->GetFields().end(); |
321 ++it) { | 351 ++it) { |
322 if (it->first->getNameAsString() == name) { | 352 if (it->second.edge()->IsWeakMember()) |
323 MarkTraced(it); | 353 it->second.MarkTraced(); |
324 break; | |
325 } | |
326 } | 354 } |
327 } else { | |
328 RecordInfo::Fields::iterator it = info_->GetFields().find(field); | |
329 if (it != info_->GetFields().end()) | |
330 MarkTraced(it); | |
331 } | 355 } |
| 356 |
| 357 QualType base = expr->getBaseType(); |
| 358 if (!base->isPointerType()) |
| 359 return true; |
| 360 CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); |
| 361 if (decl) |
| 362 CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); |
332 return true; | 363 return true; |
333 } | 364 } |
334 | 365 |
335 // If this is a weak callback function we only check field tracing. | 366 if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { |
336 if (IsWeakCallback()) | 367 if (CheckTraceFieldCall(expr) || CheckRegisterWeakMembers(expr)) |
337 return true; | 368 return true; |
| 369 } |
338 | 370 |
339 // For method calls, check tracing of bases and other special GC methods. | 371 CheckTraceBaseCall(call); |
340 if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) { | 372 return true; |
341 const string& name = fn->getNameAsString(); | 373 } |
342 // Check weak callbacks. | 374 |
343 if (name == kRegisterWeakMembersName) { | 375 private: |
344 if (fn->isTemplateInstantiation()) { | 376 |
345 const TemplateArgumentList& args = | 377 CXXRecordDecl* GetDependentTemplatedDecl(CXXDependentScopeMemberExpr* expr) { |
346 *fn->getTemplateSpecializationInfo()->TemplateArguments; | 378 NestedNameSpecifier* qual = expr->getQualifier(); |
347 // The second template argument is the callback method. | 379 if (!qual) |
348 if (args.size() > 1 && | 380 return 0; |
349 args[1].getKind() == TemplateArgument::Declaration) { | 381 |
350 if (FunctionDecl* callback = | 382 const Type* type = qual->getAsType(); |
351 dyn_cast<FunctionDecl>(args[1].getAsDecl())) { | 383 if (!type) |
352 if (callback->hasBody()) { | 384 return 0; |
353 CheckTraceVisitor nested_visitor(info_); | 385 |
354 nested_visitor.TraverseStmt(callback->getBody()); | 386 const TemplateSpecializationType* tmpl_type = |
355 } | 387 type->getAs<TemplateSpecializationType>(); |
356 } | 388 if (!tmpl_type) |
357 } | 389 return 0; |
358 } | 390 |
359 return true; | 391 TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); |
| 392 if (!tmpl_decl) |
| 393 return 0; |
| 394 |
| 395 return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); |
| 396 } |
| 397 |
| 398 void CheckCXXDependentScopeMemberExpr(CallExpr* call, |
| 399 CXXDependentScopeMemberExpr* expr) { |
| 400 string fn_name = expr->getMember().getAsString(); |
| 401 CXXRecordDecl* tmpl = GetDependentTemplatedDecl(expr); |
| 402 if (!tmpl) |
| 403 return; |
| 404 |
| 405 // Check for Super<T>::trace(visitor) |
| 406 if (call->getNumArgs() == 1 && fn_name == trace_->getName()) { |
| 407 RecordInfo::Bases::iterator it = info_->GetBases().begin(); |
| 408 for (; it != info_->GetBases().end(); ++it) { |
| 409 if (it->first->getName() == tmpl->getName()) |
| 410 it->second.MarkTraced(); |
360 } | 411 } |
| 412 return; |
| 413 } |
361 | 414 |
362 // Currently, a manually dispatched class cannot have mixin bases (having | 415 // Check for TraceIfNeeded<T>::trace(visitor, &field) |
363 // one would add a vtable which we explicitly check against). This means | 416 if (call->getNumArgs() == 2 && fn_name == kTraceName && |
364 // that we can only make calls to a trace method of the same name. Revisit | 417 tmpl->getName() == kTraceIfNeededName) { |
365 // this if our mixin/vtable assumption changes. | 418 FindFieldVisitor finder; |
366 if (Config::IsTraceMethod(fn) && | 419 finder.TraverseStmt(call->getArg(1)); |
367 fn->getName() == trace_->getName() && | 420 if (finder.field()) |
368 member->hasQualifier()) { | 421 FoundField(finder.field()); |
369 if (const Type* type = member->getQualifier()->getAsType()) { | 422 } |
370 if (CXXRecordDecl* decl = type->getAsCXXRecordDecl()) { | 423 } |
371 RecordInfo::Bases::iterator it = info_->GetBases().find(decl); | 424 |
372 if (it != info_->GetBases().end()) | 425 bool CheckTraceBaseCall(CallExpr* call) { |
373 it->second.MarkTraced(); | 426 MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee()); |
| 427 if (!callee) |
| 428 return false; |
| 429 |
| 430 FunctionDecl* fn = dyn_cast<FunctionDecl>(callee->getMemberDecl()); |
| 431 if (!fn || !Config::IsTraceMethod(fn)) |
| 432 return false; |
| 433 |
| 434 // Currently, a manually dispatched class cannot have mixin bases (having |
| 435 // one would add a vtable which we explicitly check against). This means |
| 436 // that we can only make calls to a trace method of the same name. Revisit |
| 437 // this if our mixin/vtable assumption changes. |
| 438 if (fn->getName() != trace_->getName()) |
| 439 return false; |
| 440 |
| 441 CXXRecordDecl* decl = 0; |
| 442 if (callee && callee->hasQualifier()) { |
| 443 if (const Type* type = callee->getQualifier()->getAsType()) |
| 444 decl = type->getAsCXXRecordDecl(); |
| 445 } |
| 446 if (!decl) |
| 447 return false; |
| 448 |
| 449 RecordInfo::Bases::iterator it = info_->GetBases().find(decl); |
| 450 if (it != info_->GetBases().end()) { |
| 451 it->second.MarkTraced(); |
| 452 } |
| 453 |
| 454 return true; |
| 455 } |
| 456 |
| 457 bool CheckTraceFieldCall(CXXMemberCallExpr* call) { |
| 458 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), |
| 459 call->getRecordDecl(), |
| 460 call->getArg(0)); |
| 461 } |
| 462 |
| 463 bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { |
| 464 if (name != kTraceName || !Config::IsVisitor(callee->getName())) |
| 465 return false; |
| 466 |
| 467 FindFieldVisitor finder; |
| 468 finder.TraverseStmt(arg); |
| 469 if (finder.field()) |
| 470 FoundField(finder.field()); |
| 471 |
| 472 return true; |
| 473 } |
| 474 |
| 475 bool CheckRegisterWeakMembers(CXXMemberCallExpr* call) { |
| 476 CXXMethodDecl* fn = call->getMethodDecl(); |
| 477 if (fn->getName() != kRegisterWeakMembersName) |
| 478 return false; |
| 479 |
| 480 if (fn->isTemplateInstantiation()) { |
| 481 const TemplateArgumentList& args = |
| 482 *fn->getTemplateSpecializationInfo()->TemplateArguments; |
| 483 // The second template argument is the callback method. |
| 484 if (args.size() > 1 && |
| 485 args[1].getKind() == TemplateArgument::Declaration) { |
| 486 if (FunctionDecl* callback = |
| 487 dyn_cast<FunctionDecl>(args[1].getAsDecl())) { |
| 488 if (callback->hasBody()) { |
| 489 CheckTraceVisitor nested_visitor(info_); |
| 490 nested_visitor.TraverseStmt(callback->getBody()); |
374 } | 491 } |
375 } | 492 } |
376 } | 493 } |
377 } | 494 } |
378 return true; | 495 return true; |
379 } | 496 } |
380 | 497 |
381 private: | 498 class FindFieldVisitor : public RecursiveASTVisitor<FindFieldVisitor> { |
| 499 public: |
| 500 FindFieldVisitor() : member_(0), field_(0) {} |
| 501 MemberExpr* member() const { return member_; } |
| 502 FieldDecl* field() const { return field_; } |
| 503 bool TraverseMemberExpr(MemberExpr* member) { |
| 504 if (FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl())) { |
| 505 member_ = member; |
| 506 field_ = field; |
| 507 return false; |
| 508 } |
| 509 return true; |
| 510 } |
| 511 private: |
| 512 MemberExpr* member_; |
| 513 FieldDecl* field_; |
| 514 }; |
| 515 |
382 // Nested checking for weak callbacks. | 516 // Nested checking for weak callbacks. |
383 CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} | 517 CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} |
384 | 518 |
385 bool IsWeakCallback() { return !trace_; } | 519 bool IsWeakCallback() { return !trace_; } |
386 | 520 |
387 void MarkTraced(RecordInfo::Fields::iterator it) { | 521 void MarkTraced(RecordInfo::Fields::iterator it) { |
388 // In a weak callback we can't mark strong fields as traced. | 522 // In a weak callback we can't mark strong fields as traced. |
389 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) | 523 if (IsWeakCallback() && !it->second.edge()->IsWeakMember()) |
390 return; | 524 return; |
391 it->second.MarkTraced(); | 525 it->second.MarkTraced(); |
392 } | 526 } |
393 | 527 |
| 528 void FoundField(FieldDecl* field) { |
| 529 if (IsTemplateInstantiation(info_->record())) { |
| 530 // Pointer equality on fields does not work for template instantiations. |
| 531 // The trace method refers to fields of the template definition which |
| 532 // are different from the instantiated fields that need to be traced. |
| 533 const string& name = field->getNameAsString(); |
| 534 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); |
| 535 it != info_->GetFields().end(); |
| 536 ++it) { |
| 537 if (it->first->getNameAsString() == name) { |
| 538 MarkTraced(it); |
| 539 break; |
| 540 } |
| 541 } |
| 542 } else { |
| 543 RecordInfo::Fields::iterator it = info_->GetFields().find(field); |
| 544 if (it != info_->GetFields().end()) |
| 545 MarkTraced(it); |
| 546 } |
| 547 } |
| 548 |
394 CXXMethodDecl* trace_; | 549 CXXMethodDecl* trace_; |
395 RecordInfo* info_; | 550 RecordInfo* info_; |
396 }; | 551 }; |
397 | 552 |
398 // This visitor checks that the fields of a class and the fields of | 553 // This visitor checks that the fields of a class and the fields of |
399 // its part objects don't define GC roots. | 554 // its part objects don't define GC roots. |
400 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { | 555 class CheckGCRootsVisitor : public RecursiveEdgeVisitor { |
401 public: | 556 public: |
402 typedef std::vector<FieldPoint*> RootPath; | 557 typedef std::vector<FieldPoint*> RootPath; |
403 typedef std::vector<RootPath> Errors; | 558 typedef std::vector<RootPath> Errors; |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
567 // garbage collection infrastructure. | 722 // garbage collection infrastructure. |
568 class BlinkGCPluginConsumer : public ASTConsumer { | 723 class BlinkGCPluginConsumer : public ASTConsumer { |
569 public: | 724 public: |
570 BlinkGCPluginConsumer(CompilerInstance& instance, | 725 BlinkGCPluginConsumer(CompilerInstance& instance, |
571 const BlinkGCPluginOptions& options) | 726 const BlinkGCPluginOptions& options) |
572 : instance_(instance), | 727 : instance_(instance), |
573 diagnostic_(instance.getDiagnostics()), | 728 diagnostic_(instance.getDiagnostics()), |
574 options_(options), | 729 options_(options), |
575 json_(0) { | 730 json_(0) { |
576 | 731 |
577 // Only check structures in the blink, blink and WebKit namespaces. | 732 // Only check structures in the blink and WebKit namespaces. |
578 options_.checked_namespaces.insert("blink"); | |
579 options_.checked_namespaces.insert("blink"); | 733 options_.checked_namespaces.insert("blink"); |
580 options_.checked_namespaces.insert("WebKit"); | 734 options_.checked_namespaces.insert("WebKit"); |
581 | 735 |
582 // Ignore GC implementation files. | 736 // Ignore GC implementation files. |
583 options_.ignored_directories.push_back("/heap/"); | 737 options_.ignored_directories.push_back("/heap/"); |
584 | 738 |
585 // Register warning/error messages. | 739 // Register warning/error messages. |
586 diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID( | 740 diag_class_must_left_mostly_derive_gc_ = diagnostic_.getCustomDiagID( |
587 getErrorLevel(), kClassMustLeftMostlyDeriveGC); | 741 getErrorLevel(), kClassMustLeftMostlyDeriveGC); |
588 diag_class_requires_trace_method_ = | 742 diag_class_requires_trace_method_ = |
(...skipping 888 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1477 | 1631 |
1478 private: | 1632 private: |
1479 BlinkGCPluginOptions options_; | 1633 BlinkGCPluginOptions options_; |
1480 }; | 1634 }; |
1481 | 1635 |
1482 } // namespace | 1636 } // namespace |
1483 | 1637 |
1484 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 1638 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( |
1485 "blink-gc-plugin", | 1639 "blink-gc-plugin", |
1486 "Check Blink GC invariants"); | 1640 "Check Blink GC invariants"); |
OLD | NEW |