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

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: TraceIfNeeded support 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 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
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
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");
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