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 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
194 | 194 |
195 // Collect record declarations, including nested declarations. | 195 // Collect record declarations, including nested declarations. |
196 bool VisitCXXRecordDecl(CXXRecordDecl* record) { | 196 bool VisitCXXRecordDecl(CXXRecordDecl* record) { |
197 if (record->hasDefinition() && record->isCompleteDefinition()) | 197 if (record->hasDefinition() && record->isCompleteDefinition()) |
198 record_decls_.push_back(record); | 198 record_decls_.push_back(record); |
199 return true; | 199 return true; |
200 } | 200 } |
201 | 201 |
202 // Collect tracing method definitions, but don't traverse method bodies. | 202 // Collect tracing method definitions, but don't traverse method bodies. |
203 bool TraverseCXXMethodDecl(CXXMethodDecl* method) { | 203 bool TraverseCXXMethodDecl(CXXMethodDecl* method) { |
204 if (method->isThisDeclarationADefinition() && | 204 if (method->isThisDeclarationADefinition() && Config::IsTraceMethod(method)) |
205 Config::IsTraceMethod(method, nullptr)) | |
206 trace_decls_.push_back(method); | 205 trace_decls_.push_back(method); |
207 return true; | 206 return true; |
208 } | 207 } |
209 | 208 |
210 private: | 209 private: |
211 RecordVector record_decls_; | 210 RecordVector record_decls_; |
212 MethodVector trace_decls_; | 211 MethodVector trace_decls_; |
213 }; | 212 }; |
214 | 213 |
215 // This visitor checks that a finalizer method does not have invalid access to | 214 // This visitor checks that a finalizer method does not have invalid access to |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 return true; | 358 return true; |
360 } | 359 } |
361 | 360 |
362 // A tracing call will have either a |visitor| or a |m_field| argument. | 361 // A tracing call will have either a |visitor| or a |m_field| argument. |
363 // A registerWeakMembers call will have a |this| argument. | 362 // A registerWeakMembers call will have a |this| argument. |
364 if (call->getNumArgs() != 1) | 363 if (call->getNumArgs() != 1) |
365 return true; | 364 return true; |
366 Expr* arg = call->getArg(0); | 365 Expr* arg = call->getArg(0); |
367 | 366 |
368 if (UnresolvedMemberExpr* expr = dyn_cast<UnresolvedMemberExpr>(callee)) { | 367 if (UnresolvedMemberExpr* expr = dyn_cast<UnresolvedMemberExpr>(callee)) { |
| 368 // This could be a trace call of a base class, as explained in the |
| 369 // comments of CheckTraceBaseCall(). |
| 370 if (CheckTraceBaseCall(call)) |
| 371 return true; |
| 372 |
369 // If we find a call to registerWeakMembers which is unresolved we | 373 // If we find a call to registerWeakMembers which is unresolved we |
370 // unsoundly consider all weak members as traced. | 374 // unsoundly consider all weak members as traced. |
371 // TODO: Find out how to validate weak member tracing for unresolved call. | 375 // TODO: Find out how to validate weak member tracing for unresolved call. |
372 if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) { | 376 if (expr->getMemberName().getAsString() == kRegisterWeakMembersName) { |
373 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); | 377 for (RecordInfo::Fields::iterator it = info_->GetFields().begin(); |
374 it != info_->GetFields().end(); | 378 it != info_->GetFields().end(); |
375 ++it) { | 379 ++it) { |
376 if (it->second.edge()->IsWeakMember()) | 380 if (it->second.edge()->IsWeakMember()) |
377 it->second.MarkTraced(); | 381 it->second.MarkTraced(); |
378 } | 382 } |
379 } | 383 } |
380 | 384 |
381 QualType base = expr->getBaseType(); | 385 QualType base = expr->getBaseType(); |
382 if (!base->isPointerType()) | 386 if (!base->isPointerType()) |
383 return true; | 387 return true; |
384 CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); | 388 CXXRecordDecl* decl = base->getPointeeType()->getAsCXXRecordDecl(); |
385 if (decl) | 389 if (decl) |
386 CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); | 390 CheckTraceFieldCall(expr->getMemberName().getAsString(), decl, arg); |
387 if (expr->getMemberName().getAsString() == kTraceImplName) | 391 if (Config::IsTraceImplName(expr->getMemberName().getAsString())) |
388 delegates_to_traceimpl_ = true; | 392 delegates_to_traceimpl_ = true; |
389 return true; | 393 return true; |
390 } | 394 } |
391 | 395 |
392 if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { | 396 if (CXXMemberCallExpr* expr = dyn_cast<CXXMemberCallExpr>(call)) { |
393 if (CheckTraceFieldCall(expr) || CheckRegisterWeakMembers(expr)) | 397 if (CheckTraceFieldCall(expr) || CheckRegisterWeakMembers(expr)) |
394 return true; | 398 return true; |
395 | 399 |
396 if (expr->getMethodDecl()->getNameAsString() == kTraceImplName) { | 400 if (Config::IsTraceImplName(expr->getMethodDecl()->getNameAsString())) { |
397 delegates_to_traceimpl_ = true; | 401 delegates_to_traceimpl_ = true; |
398 return true; | 402 return true; |
399 } | 403 } |
400 } | 404 } |
401 | 405 |
402 CheckTraceBaseCall(call); | 406 CheckTraceBaseCall(call); |
403 return true; | 407 return true; |
404 } | 408 } |
405 | 409 |
406 private: | 410 private: |
407 | |
408 CXXRecordDecl* GetDependentTemplatedDecl(CXXDependentScopeMemberExpr* expr) { | 411 CXXRecordDecl* GetDependentTemplatedDecl(CXXDependentScopeMemberExpr* expr) { |
409 NestedNameSpecifier* qual = expr->getQualifier(); | 412 NestedNameSpecifier* qual = expr->getQualifier(); |
410 if (!qual) | 413 if (!qual) |
411 return 0; | 414 return 0; |
412 | 415 |
413 const Type* type = qual->getAsType(); | 416 const Type* type = qual->getAsType(); |
414 if (!type) | 417 if (!type) |
415 return 0; | 418 return 0; |
416 | 419 |
417 return RecordInfo::GetDependentTemplatedDecl(*type); | 420 return RecordInfo::GetDependentTemplatedDecl(*type); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
455 if (call->getNumArgs() == 2 && fn_name == kTraceName && | 458 if (call->getNumArgs() == 2 && fn_name == kTraceName && |
456 tmpl->getName() == kTraceIfNeededName) { | 459 tmpl->getName() == kTraceIfNeededName) { |
457 FindFieldVisitor finder; | 460 FindFieldVisitor finder; |
458 finder.TraverseStmt(call->getArg(1)); | 461 finder.TraverseStmt(call->getArg(1)); |
459 if (finder.field()) | 462 if (finder.field()) |
460 FoundField(finder.field()); | 463 FoundField(finder.field()); |
461 } | 464 } |
462 } | 465 } |
463 | 466 |
464 bool CheckTraceBaseCall(CallExpr* call) { | 467 bool CheckTraceBaseCall(CallExpr* call) { |
465 MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee()); | 468 // Checks for "Base::trace(visitor)"-like calls. |
466 if (!callee) | |
467 return false; | |
468 | 469 |
469 FunctionDecl* fn = dyn_cast<FunctionDecl>(callee->getMemberDecl()); | 470 // Checking code for these two variables is shared among MemberExpr* case |
470 if (!fn || !Config::IsTraceMethod(fn, nullptr)) | 471 // and UnresolvedMemberCase* case below. |
471 return false; | 472 // |
| 473 // For example, if we've got "Base::trace(visitor)" as |call|, |
| 474 // callee_record will be "Base", and func_name will be "trace". |
| 475 CXXRecordDecl* callee_record = nullptr; |
| 476 std::string func_name; |
| 477 |
| 478 if (MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee())) { |
| 479 if (!callee->hasQualifier()) |
| 480 return false; |
| 481 |
| 482 FunctionDecl* trace_decl = |
| 483 dyn_cast<FunctionDecl>(callee->getMemberDecl()); |
| 484 if (!trace_decl || !Config::IsTraceMethod(trace_decl)) |
| 485 return false; |
| 486 |
| 487 const Type* type = callee->getQualifier()->getAsType(); |
| 488 if (!type) |
| 489 return false; |
| 490 |
| 491 callee_record = type->getAsCXXRecordDecl(); |
| 492 func_name = trace_decl->getName(); |
| 493 } else if (UnresolvedMemberExpr* callee = |
| 494 dyn_cast<UnresolvedMemberExpr>(call->getCallee())) { |
| 495 // Callee part may become unresolved if the type of the argument |
| 496 // ("visitor") is a template parameter and the called function is |
| 497 // overloaded (i.e. trace(Visitor*) and |
| 498 // trace(InlinedGlobalMarkingVisitor)). |
| 499 // |
| 500 // Here, we try to find a function that looks like trace() from the |
| 501 // candidate overloaded functions, and if we find one, we assume it is |
| 502 // called here. |
| 503 |
| 504 CXXMethodDecl* trace_decl = nullptr; |
| 505 for (NamedDecl* named_decl : callee->decls()) { |
| 506 if (CXXMethodDecl* method_decl = dyn_cast<CXXMethodDecl>(named_decl)) { |
| 507 if (Config::IsTraceMethod(method_decl)) { |
| 508 trace_decl = method_decl; |
| 509 break; |
| 510 } |
| 511 } |
| 512 } |
| 513 if (!trace_decl) |
| 514 return false; |
| 515 |
| 516 // Check if the passed argument is named "visitor". |
| 517 if (call->getNumArgs() != 1) |
| 518 return false; |
| 519 DeclRefExpr* arg = dyn_cast<DeclRefExpr>(call->getArg(0)); |
| 520 if (!arg || arg->getNameInfo().getAsString() != kVisitorVarName) |
| 521 return false; |
| 522 |
| 523 callee_record = trace_decl->getParent(); |
| 524 func_name = callee->getMemberName().getAsString(); |
| 525 } |
472 | 526 |
473 if (trace_->getName() == kTraceImplName) { | 527 if (trace_->getName() == kTraceImplName) { |
474 if (fn->getName() != kTraceName) | 528 if (func_name != kTraceName) |
| 529 return false; |
| 530 } else if (trace_->getName() == kTraceAfterDispatchImplName) { |
| 531 if (func_name != kTraceAfterDispatchName) |
475 return false; | 532 return false; |
476 } else { | 533 } else { |
477 // Currently, a manually dispatched class cannot have mixin bases (having | 534 // Currently, a manually dispatched class cannot have mixin bases (having |
478 // one would add a vtable which we explicitly check against). This means | 535 // one would add a vtable which we explicitly check against). This means |
479 // that we can only make calls to a trace method of the same name. Revisit | 536 // that we can only make calls to a trace method of the same name. Revisit |
480 // this if our mixin/vtable assumption changes. | 537 // this if our mixin/vtable assumption changes. |
481 if (fn->getName() != trace_->getName()) | 538 if (func_name != trace_->getName()) |
482 return false; | 539 return false; |
483 } | 540 } |
484 | 541 |
485 CXXRecordDecl* decl = 0; | 542 if (!callee_record) |
486 if (callee && callee->hasQualifier()) { | 543 return false; |
487 if (const Type* type = callee->getQualifier()->getAsType()) | 544 RecordInfo::Bases::iterator iter = info_->GetBases().find(callee_record); |
488 decl = type->getAsCXXRecordDecl(); | 545 if (iter == info_->GetBases().end()) |
489 } | |
490 if (!decl) | |
491 return false; | 546 return false; |
492 | 547 |
493 RecordInfo::Bases::iterator it = info_->GetBases().find(decl); | 548 iter->second.MarkTraced(); |
494 if (it != info_->GetBases().end()) { | |
495 it->second.MarkTraced(); | |
496 } | |
497 | |
498 return true; | 549 return true; |
499 } | 550 } |
500 | 551 |
501 bool CheckTraceFieldCall(CXXMemberCallExpr* call) { | 552 bool CheckTraceFieldCall(CXXMemberCallExpr* call) { |
502 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), | 553 return CheckTraceFieldCall(call->getMethodDecl()->getNameAsString(), |
503 call->getRecordDecl(), | 554 call->getRecordDecl(), |
504 call->getArg(0)); | 555 call->getArg(0)); |
505 } | 556 } |
506 | 557 |
507 bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { | 558 bool CheckTraceFieldCall(string name, CXXRecordDecl* callee, Expr* arg) { |
(...skipping 798 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1306 CheckTraceOrDispatchMethod(cache_.Lookup(*it), method); | 1357 CheckTraceOrDispatchMethod(cache_.Lookup(*it), method); |
1307 } | 1358 } |
1308 return; | 1359 return; |
1309 } | 1360 } |
1310 | 1361 |
1311 CheckTraceOrDispatchMethod(parent, method); | 1362 CheckTraceOrDispatchMethod(parent, method); |
1312 } | 1363 } |
1313 | 1364 |
1314 // Determine what type of tracing method this is (dispatch or trace). | 1365 // Determine what type of tracing method this is (dispatch or trace). |
1315 void CheckTraceOrDispatchMethod(RecordInfo* parent, CXXMethodDecl* method) { | 1366 void CheckTraceOrDispatchMethod(RecordInfo* parent, CXXMethodDecl* method) { |
1316 bool isTraceAfterDispatch; | 1367 Config::TraceMethodType trace_type = Config::GetTraceMethodType(method); |
1317 if (Config::IsTraceMethod(method, &isTraceAfterDispatch)) { | 1368 if (trace_type != Config::TRACE_METHOD || |
1318 if (isTraceAfterDispatch || !parent->GetTraceDispatchMethod()) { | 1369 !parent->GetTraceDispatchMethod()) { |
1319 CheckTraceMethod(parent, method, isTraceAfterDispatch); | 1370 CheckTraceMethod(parent, method, trace_type); |
1320 } | |
1321 // Dispatch methods are checked when we identify subclasses. | |
1322 } | 1371 } |
| 1372 // Dispatch methods are checked when we identify subclasses. |
1323 } | 1373 } |
1324 | 1374 |
1325 // Check an actual trace method. | 1375 // Check an actual trace method. |
1326 void CheckTraceMethod(RecordInfo* parent, | 1376 void CheckTraceMethod(RecordInfo* parent, |
1327 CXXMethodDecl* trace, | 1377 CXXMethodDecl* trace, |
1328 bool isTraceAfterDispatch) { | 1378 Config::TraceMethodType trace_type) { |
1329 // A trace method must not override any non-virtual trace methods. | 1379 // A trace method must not override any non-virtual trace methods. |
1330 if (!isTraceAfterDispatch) { | 1380 if (trace_type == Config::TRACE_METHOD) { |
1331 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1381 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
1332 it != parent->GetBases().end(); | 1382 it != parent->GetBases().end(); |
1333 ++it) { | 1383 ++it) { |
1334 RecordInfo* base = it->second.info(); | 1384 RecordInfo* base = it->second.info(); |
1335 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) | 1385 if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
1336 ReportOverriddenNonVirtualTrace(parent, trace, other); | 1386 ReportOverriddenNonVirtualTrace(parent, trace, other); |
1337 } | 1387 } |
1338 } | 1388 } |
1339 | 1389 |
1340 CheckTraceVisitor visitor(trace, parent); | 1390 CheckTraceVisitor visitor(trace, parent); |
1341 visitor.TraverseCXXMethodDecl(trace); | 1391 visitor.TraverseCXXMethodDecl(trace); |
1342 | 1392 |
1343 // Skip reporting if this trace method is a just delegate to | 1393 // Skip reporting if this trace method is a just delegate to |
1344 // traceImpl method. We will report on CheckTraceMethod on traceImpl method. | 1394 // traceImpl (or traceAfterDispatchImpl) method. We will report on |
| 1395 // CheckTraceMethod on traceImpl method. |
1345 if (visitor.delegates_to_traceimpl()) | 1396 if (visitor.delegates_to_traceimpl()) |
1346 return; | 1397 return; |
1347 | 1398 |
1348 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); | 1399 for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
1349 it != parent->GetBases().end(); | 1400 it != parent->GetBases().end(); |
1350 ++it) { | 1401 ++it) { |
1351 if (!it->second.IsProperlyTraced()) | 1402 if (!it->second.IsProperlyTraced()) |
1352 ReportBaseRequiresTracing(parent, trace, it->first); | 1403 ReportBaseRequiresTracing(parent, trace, it->first); |
1353 } | 1404 } |
1354 | 1405 |
(...skipping 584 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1939 | 1990 |
1940 private: | 1991 private: |
1941 BlinkGCPluginOptions options_; | 1992 BlinkGCPluginOptions options_; |
1942 }; | 1993 }; |
1943 | 1994 |
1944 } // namespace | 1995 } // namespace |
1945 | 1996 |
1946 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( | 1997 static FrontendPluginRegistry::Add<BlinkGCPluginAction> X( |
1947 "blink-gc-plugin", | 1998 "blink-gc-plugin", |
1948 "Check Blink GC invariants"); | 1999 "Check Blink GC invariants"); |
OLD | NEW |