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