Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(629)

Side by Side Diff: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp

Issue 430213003: Blink GC plugin: Require that fields are actually traced by the visitor and identify tracing of dep… (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Config.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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");
OLDNEW
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Config.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698