OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "PostTaskTransform.h" |
| 6 |
| 7 #include "clang/AST/ASTContext.h" |
| 8 #include "clang/AST/Expr.h" |
| 9 #include "clang/AST/ExprCXX.h" |
| 10 #include "clang/Rewrite/Rewriter.h" |
| 11 |
| 12 #include "DiagnosticEmitter.h" |
| 13 |
| 14 using namespace std; |
| 15 |
| 16 namespace clang { |
| 17 |
| 18 PostTaskTransform::PostTaskTransform(ASTContext* context, Rewriter* rewriter, |
| 19 DiagnosticEmitter* emitter) |
| 20 : context_(context), rewriter_(rewriter), error_emitter_(emitter) { |
| 21 interesting_classes_.insert("MessageLoop"); |
| 22 interesting_classes_.insert("MessageLoopProxy"); |
| 23 } |
| 24 |
| 25 bool PostTaskTransform::WouldTransformStmt(Stmt* statement) { |
| 26 return IsPostTaskNRMExpr(statement); |
| 27 } |
| 28 |
| 29 void PostTaskTransform::TransformStmt(Stmt* statement) { |
| 30 if (!IsPostTaskNRMExpr(statement)) return; |
| 31 |
| 32 // Catch the MessageLoop and MessageLoopProxy calls. |
| 33 CXXMemberCallExpr* mce = dyn_cast<CXXMemberCallExpr>(statement); |
| 34 RewriteIfNewRunnableSomething(mce->getArgs()[1]); |
| 35 } |
| 36 |
| 37 bool PostTaskTransform::IsPostTaskNRMExpr(Stmt* statement) { |
| 38 CXXMemberCallExpr* call = dyn_cast_or_null<CXXMemberCallExpr>(statement); |
| 39 if (!call) return false; |
| 40 CXXMethodDecl* method_decl = call->getMethodDecl(); |
| 41 if (!method_decl) return false; |
| 42 method_decl = method_decl->getCanonicalDecl(); |
| 43 if ("PostTask" != method_decl->getNameAsString()) { |
| 44 return false; |
| 45 } |
| 46 |
| 47 string classname = method_decl->getThisType(*context_) |
| 48 .getBaseTypeIdentifier()->getName(); |
| 49 |
| 50 if (interesting_classes_.find(classname) == interesting_classes_.end()) |
| 51 return false; |
| 52 |
| 53 if (call->getNumArgs() < 2) { |
| 54 error_emitter_->EmitError( |
| 55 statement->getLocStart(), |
| 56 "PostTask with less than 2 args?! Inconceivable!"); |
| 57 return false; |
| 58 } |
| 59 |
| 60 // Check that PostTask second arg is actually the ::NewRunnableMethod() call. |
| 61 CallExpr* nrm_ce = dyn_cast<CallExpr>(call->getArgs()[1]); |
| 62 if (!nrm_ce) return false; |
| 63 if (dyn_cast<CXXMemberCallExpr>(nrm_ce)) return false; |
| 64 FunctionDecl *nrm_fd = nrm_ce->getDirectCallee(); |
| 65 if (!nrm_fd) return false; |
| 66 std::string nrm_function_name = |
| 67 nrm_fd->getNameInfo().getName().getAsIdentifierInfo()->getName(); |
| 68 if ("NewRunnableMethod" != nrm_function_name) { |
| 69 return false; |
| 70 } |
| 71 |
| 72 if (nrm_ce->getNumArgs() < 2) { |
| 73 error_emitter_->EmitWarning( |
| 74 nrm_ce->getLocStart(), |
| 75 "NewRunnableMethod with less than 2 args?! Inconceivable!"); |
| 76 return false; |
| 77 } |
| 78 |
| 79 return true; |
| 80 } |
| 81 |
| 82 static bool NoAddrefRelease(const CXXRecordDecl *record, void *user_data) { |
| 83 bool found_addref = false; |
| 84 bool found_release = false; |
| 85 |
| 86 for (CXXRecordDecl::method_iterator it = record->method_begin(), |
| 87 end = record->method_end(); it != end; ++it) { |
| 88 if (it->getAccess() == AS_public) { |
| 89 found_addref |= "AddRef" == it->getNameAsString(); |
| 90 found_release |= "Release" == it->getNameAsString(); |
| 91 } |
| 92 } |
| 93 |
| 94 return !(found_addref && found_release); |
| 95 } |
| 96 |
| 97 bool PostTaskTransform::IsRefcountedType(QualType type) { |
| 98 CXXRecordDecl* record = type->getAsCXXRecordDecl(); |
| 99 return !(NoAddrefRelease(record, NULL) && |
| 100 record->forallBases(&NoAddrefRelease, NULL, true)); |
| 101 } |
| 102 |
| 103 bool PostTaskTransform::RewriteIfNewRunnableSomething(Expr* post_task_arg) { |
| 104 // Skip any implicit conversions and any casts. We wantz the function!! |
| 105 post_task_arg = post_task_arg->IgnoreImplicit()->IgnoreParenCasts(); |
| 106 |
| 107 CallExpr* ce = dyn_cast<CallExpr>(post_task_arg); |
| 108 if (!ce) return false; |
| 109 FunctionDecl *fd = ce->getDirectCallee(); |
| 110 if (!fd) return false; |
| 111 |
| 112 // Okay, here's where it gets fun. We need to |
| 113 // (1) replace the NRM identifier text with base::Bind. |
| 114 // (2) Swap positions of the first two arguments. |
| 115 // (3) If we're calling a method, and the class is not refcounted, then |
| 116 // a DISABLE_RUNNABLE_METHOD_REFCOUNT or similar must have been used |
| 117 // to disable refcounting. Insert a base::Unretained(). |
| 118 |
| 119 // NewRunnableMethod -> base::Bind |
| 120 bool failure = rewriter_->ReplaceText( |
| 121 ce->getCallee()->getSourceRange(), "base::Bind"); |
| 122 |
| 123 Expr* arg1 = ce->getArgs()[0]; |
| 124 Expr* arg2 = ce->getArgs()[1]; |
| 125 |
| 126 |
| 127 // Swap args, and add unretained to the object if needed. |
| 128 failure |= rewriter_->ReplaceStmt(arg1, arg2); |
| 129 if (arg2->getType()->isMemberFunctionPointerType() && |
| 130 arg1->getType()->isPointerType() && |
| 131 arg1->getType()->getPointeeType()->isStructureOrClassType() && |
| 132 !IsRefcountedType(arg1->getType()->getPointeeType())) { |
| 133 // The rewriter works in the context of the soruce buffer. Though it |
| 134 // tries to be smart about mapping the source location into the |
| 135 // destination over multiple rewrites, it can get confused if you try |
| 136 // to modify the same region of text multiple times. Doing things in |
| 137 // order from "left to right" seems to keep the Rewritter happier. |
| 138 // |
| 139 // TODO(ajwong): See if the ARCMigrate source transaction system that |
| 140 // batches and combines modificaitons to be applied at the end can |
| 141 // be generalized and reused here. |
| 142 std::string unretained_arg("base::Unretained("); |
| 143 unretained_arg += rewriter_->ConvertToString(arg1); |
| 144 unretained_arg += ")"; |
| 145 failure |= rewriter_->ReplaceText(arg2->getSourceRange(), unretained_arg); |
| 146 } else { |
| 147 failure |= rewriter_->ReplaceStmt(arg2, arg1); |
| 148 } |
| 149 return failure; |
| 150 } |
| 151 |
| 152 } // namespace clang |
OLD | NEW |