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

Side by Side Diff: tools/clang/BindMigrate/PostTaskTransform.cpp

Issue 7886056: Clang plugin that rewrites PostTask(_, NewRunnableMethod(...)) to PostTask(_, base::Bind(...)); (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix lifetime issue where BindMigrateAction is deleted before the ASTConsumer is finished. Created 9 years, 2 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 | « tools/clang/BindMigrate/PostTaskTransform.h ('k') | tools/clang/BindMigrate/README.chromium » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « tools/clang/BindMigrate/PostTaskTransform.h ('k') | tools/clang/BindMigrate/README.chromium » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698