OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 // This tool performs the following transformations on base::MessageLoop and | |
6 // related APIs: | |
7 // | |
8 // MessageLoop: | |
9 // - base::MessageLoop::PostTask* | |
10 // => base::MessageLoop::task_runner()->PostTask*() | |
dcheng
2015/05/06 17:31:18
Dumb question: why not just update MessageLoop::Po
Sami
2015/05/07 10:37:37
There is some more discussion on the linked design
| |
11 // | |
12 // Thread: | |
13 // - base::Thread::message_loop_proxy() | |
14 // => task_runner() | |
15 // | |
16 // MessageLoopProxy: | |
17 // - base::MessageLoopProxy | |
18 // => base::SingleThreadTaskRunner | |
19 // - scoped_refptr<base::MessageLoopProxy> | |
20 // => scoped_refptr<base::SingleThreadTaskRunner> | |
21 // - base::MessageLoopProxy::current() | |
22 // => base::ThreadTaskRunnerHandle::Get() | |
23 // - base::MessageLoop::message_loop_proxy() | |
24 // => base::MessageLoop::task_runner() (done) | |
25 // | |
26 | |
27 #include <memory> | |
28 #include "clang/AST/ExprCXX.h" | |
29 #include "clang/ASTMatchers/ASTMatchers.h" | |
30 #include "clang/ASTMatchers/ASTMatchFinder.h" | |
31 #include "clang/Basic/SourceManager.h" | |
32 #include "clang/Frontend/FrontendActions.h" | |
33 #include "clang/Lex/Lexer.h" | |
34 #include "clang/Tooling/CommonOptionsParser.h" | |
35 #include "clang/Tooling/Refactoring.h" | |
36 #include "clang/Tooling/Tooling.h" | |
37 #include "llvm/Support/CommandLine.h" | |
38 | |
39 using clang::ast_matchers::MatchFinder; | |
40 using clang::ast_matchers::anyOf; | |
41 using clang::ast_matchers::asString; | |
42 using clang::ast_matchers::callExpr; | |
43 using clang::ast_matchers::callee; | |
44 using clang::ast_matchers::decl; | |
45 using clang::ast_matchers::functionDecl; | |
46 using clang::ast_matchers::hasAncestor; | |
47 using clang::ast_matchers::hasAnyTemplateArgument; | |
48 using clang::ast_matchers::hasDescendant; | |
49 using clang::ast_matchers::hasName; | |
50 using clang::ast_matchers::hasType; | |
51 using clang::ast_matchers::isSameOrDerivedFrom; | |
52 using clang::ast_matchers::isTemplateInstantiation; | |
53 using clang::ast_matchers::memberCallExpr; | |
54 using clang::ast_matchers::methodDecl; | |
55 using clang::ast_matchers::ofClass; | |
56 using clang::ast_matchers::pointsTo; | |
57 using clang::ast_matchers::recordDecl; | |
58 using clang::ast_matchers::refersToType; | |
59 using clang::ast_matchers::templateSpecializationType; | |
60 using clang::ast_matchers::thisPointerType; | |
61 using clang::ast_matchers::unless; | |
62 using clang::ast_matchers::varDecl; | |
63 | |
64 using clang::tooling::CommonOptionsParser; | |
65 using clang::tooling::Replacement; | |
66 using clang::tooling::Replacements; | |
67 | |
68 using clang::CXXMemberCallExpr; | |
69 using clang::CallExpr; | |
70 using clang::Expr; | |
71 using clang::MemberExpr; | |
72 using clang::VarDecl; | |
73 | |
74 namespace { | |
75 | |
76 std::string ReplaceFirst(const std::string& input, | |
dcheng
2015/05/06 17:31:18
Consider using llvm::StringRef where appropriate,
Sami
2015/05/07 10:37:37
Neat, thanks for the tip. Done.
| |
77 const char* from, | |
78 const char* to) { | |
79 size_t pos = input.find(from); | |
80 if (pos == std::string::npos) | |
81 return input; | |
82 return input.substr(0, pos) + to + input.substr(pos + strlen(from)); | |
83 } | |
84 | |
85 // Handles conversion of the Post*Task APIs. | |
86 class PostTaskCallback : public MatchFinder::MatchCallback { | |
87 public: | |
88 PostTaskCallback(Replacements* replacements) : replacements_(replacements) {} | |
dcheng
2015/05/06 17:31:18
explicit
Sami
2015/05/07 10:37:37
Fixed (here and elsewhere).
| |
89 | |
90 virtual void run(const MatchFinder::MatchResult& result) override; | |
dcheng
2015/05/06 17:31:17
No virtual.
Sami
2015/05/07 10:37:37
Fixed (here and elsewhere).
| |
91 | |
92 private: | |
93 Replacements* const replacements_; | |
94 }; | |
95 | |
96 // Refactors base::MessageLoopProxy::current() callers. | |
97 class CurrentProxyCallback : public MatchFinder::MatchCallback { | |
98 public: | |
99 CurrentProxyCallback(Replacements* replacements) | |
100 : replacements_(replacements) {} | |
101 | |
102 virtual void run(const MatchFinder::MatchResult& result) override; | |
103 | |
104 private: | |
105 Replacements* const replacements_; | |
106 }; | |
107 | |
108 // Refactors callers to base::MessageLoop::message_loop_proxy() and | |
109 // base::Thread::message_loop_proxy(). | |
110 class ProxyGetterCallback : public MatchFinder::MatchCallback { | |
111 public: | |
112 ProxyGetterCallback(Replacements* replacements) | |
113 : replacements_(replacements) {} | |
114 | |
115 virtual void run(const MatchFinder::MatchResult& result) override; | |
116 | |
117 private: | |
118 Replacements* const replacements_; | |
119 }; | |
120 | |
121 // Rewrites variables of type MessageLoopProxy*. | |
122 class ProxyVariableCallback : public MatchFinder::MatchCallback { | |
123 public: | |
124 ProxyVariableCallback(Replacements* replacements) | |
125 : replacements_(replacements) {} | |
126 | |
127 virtual void run(const MatchFinder::MatchResult& result) override; | |
128 | |
129 private: | |
130 Replacements* const replacements_; | |
131 }; | |
132 | |
133 // Rewrites variables of type scoped_refptr<MessageLoopProxy>. | |
134 class RefPtrProxyVariableCallback : public MatchFinder::MatchCallback { | |
135 public: | |
136 RefPtrProxyVariableCallback(Replacements* replacements) | |
137 : replacements_(replacements) {} | |
138 | |
139 virtual void run(const MatchFinder::MatchResult& result) override; | |
140 | |
141 private: | |
142 Replacements* const replacements_; | |
143 }; | |
144 | |
145 class MessageLoopRefactorer { | |
146 public: | |
147 explicit MessageLoopRefactorer(Replacements* replacements) | |
148 : post_callback_(replacements), | |
149 current_proxy_callback_(replacements), | |
150 proxy_getter_callback_(replacements), | |
151 proxy_variable_callback_(replacements), | |
152 refptr_proxy_variable_callback_(replacements) {} | |
153 | |
154 void SetupMatchers(MatchFinder* match_finder); | |
155 | |
156 private: | |
157 PostTaskCallback post_callback_; | |
158 CurrentProxyCallback current_proxy_callback_; | |
159 ProxyGetterCallback proxy_getter_callback_; | |
160 ProxyVariableCallback proxy_variable_callback_; | |
161 RefPtrProxyVariableCallback refptr_proxy_variable_callback_; | |
162 }; | |
163 | |
164 void MessageLoopRefactorer::SetupMatchers(MatchFinder* match_finder) { | |
165 auto is_message_loop = recordDecl(isSameOrDerivedFrom("base::MessageLoop")); | |
166 auto is_thread = recordDecl(isSameOrDerivedFrom("base::Thread")); | |
167 auto is_message_loop_proxy = | |
168 recordDecl(isSameOrDerivedFrom("base::MessageLoopProxy")); | |
169 | |
170 // Matches calls to the Post*Task APIs. | |
171 auto post_matcher = | |
172 memberCallExpr(thisPointerType(is_message_loop), | |
173 callee(methodDecl(anyOf( | |
174 hasName("PostTask"), hasName("PostDelayedTask"), | |
175 hasName("PostNonNestableTask"), | |
176 hasName("PostNonNestableDelayedTask"))))).bind("call"); | |
177 | |
178 // Matches calls to MessageLoopProxy::current(). | |
179 auto current_proxy_matcher = | |
180 callExpr(callee(methodDecl(ofClass(is_message_loop_proxy), | |
181 hasName("current")))).bind("call"); | |
182 | |
183 auto message_loop_proxy_callee = | |
184 callee(methodDecl(hasName("message_loop_proxy"))); | |
185 | |
186 // Matches calls to MessageLoop::message_loop_proxy(). | |
187 auto loop_proxy_getter_matcher = | |
188 memberCallExpr(thisPointerType(is_message_loop), | |
189 message_loop_proxy_callee).bind("call"); | |
190 | |
191 // Matches calls to Thread::message_loop_proxy(). | |
192 auto thread_proxy_getter_matcher = | |
193 memberCallExpr(thisPointerType(is_thread), message_loop_proxy_callee) | |
194 .bind("call"); | |
195 | |
196 // Matches variables pointing to a MessageLoopProxy that aren't inside | |
197 // template instantiations (e.g., scoped_retpr<>). | |
198 auto proxy_variable_matcher = | |
199 varDecl(hasType(pointsTo(recordDecl(hasName("base::MessageLoopProxy")))), | |
200 unless(hasAncestor(decl(anyOf( | |
201 recordDecl(isTemplateInstantiation()), | |
202 functionDecl(isTemplateInstantiation())))))).bind("var"); | |
203 | |
204 // Matches scoped_refptr<MessageLoopProxy>. | |
205 auto refptr_proxy_variable_matcher = | |
206 varDecl(hasType(recordDecl(hasName("::scoped_refptr"))), | |
207 hasDescendant(templateSpecializationType(hasAnyTemplateArgument( | |
208 refersToType(asString("class base::MessageLoopProxy")))))) | |
209 .bind("var"); | |
210 | |
211 match_finder->addMatcher(current_proxy_matcher, ¤t_proxy_callback_); | |
212 match_finder->addMatcher(loop_proxy_getter_matcher, &proxy_getter_callback_); | |
213 match_finder->addMatcher(thread_proxy_getter_matcher, | |
214 &proxy_getter_callback_); | |
215 match_finder->addMatcher(post_matcher, &post_callback_); | |
216 match_finder->addMatcher(proxy_variable_matcher, &proxy_variable_callback_); | |
217 match_finder->addMatcher(refptr_proxy_variable_matcher, | |
218 &refptr_proxy_variable_callback_); | |
219 } | |
220 | |
221 void PostTaskCallback::run(const MatchFinder::MatchResult& result) { | |
222 const CXXMemberCallExpr* call = | |
223 result.Nodes.getNodeAs<CXXMemberCallExpr>("call"); | |
224 const Expr* obj = call->getImplicitObjectArgument(); | |
225 const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee()); | |
226 | |
227 clang::CharSourceRange range = | |
228 clang::CharSourceRange::getTokenRange(callee->getSourceRange()); | |
229 clang::CharSourceRange obj_range = | |
230 clang::CharSourceRange::getTokenRange(obj->getSourceRange()); | |
231 | |
232 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
233 result.Context->getLangOpts()); | |
234 std::string obj_text = clang::Lexer::getSourceText( | |
235 obj_range, *result.SourceManager, result.Context->getLangOpts()); | |
236 std::string method = call->getMethodDecl()->getNameInfo().getAsString(); | |
237 | |
238 // Rewrite the call to go through the task runner. | |
239 bool is_arrow = callee->isArrow(); | |
240 std::string replacement = | |
241 obj_text + (is_arrow ? "->" : ".") + "task_runner()->" + method; | |
dcheng
2015/05/06 17:31:17
Isn't this always -> in Chrome?
Sami
2015/05/07 10:37:37
That's almost always the case, but some tests do h
| |
242 | |
243 // Rewrite MessageLoop::current() as ThreadTaskRunnerHandle::Get(). | |
dcheng
2015/05/06 17:31:18
Why is this chunk here? I would have expected to s
Sami
2015/05/07 10:37:37
CurrentProxyCallback matches calls to MessageLoopP
| |
244 if (obj_text.find("MessageLoop::current") != obj_text.npos || | |
245 obj_text.find("MessageLoopForIO::current") != obj_text.npos || | |
246 obj_text.find("MessageLoopForUI::current") != obj_text.npos) { | |
247 std::string ns = ""; | |
248 if (obj_text.find("base::") == 0) { | |
249 ns = "base::"; | |
250 } | |
251 replacement = ns + "ThreadTaskRunnerHandle::Get()->" + method; | |
252 } | |
253 | |
254 replacements_->insert(Replacement(*result.SourceManager, range, replacement)); | |
255 } | |
256 | |
257 void CurrentProxyCallback::run(const MatchFinder::MatchResult& result) { | |
258 const CallExpr* call = result.Nodes.getNodeAs<CallExpr>("call"); | |
259 clang::CharSourceRange range = | |
260 clang::CharSourceRange::getTokenRange(call->getSourceRange()); | |
261 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
262 result.Context->getLangOpts()); | |
263 | |
264 std::string ns = ""; | |
265 if (text.find("base::") == 0) { | |
dcheng
2015/05/06 17:31:18
Hmm... I guess I should finish my patch to try to
Sami
2015/05/07 10:37:37
Got a link? Sounds useful :)
Although for this pa
| |
266 ns = "base::"; | |
267 } | |
268 std::string replacement = ns + "ThreadTaskRunnerHandle::Get()"; | |
269 replacements_->insert(Replacement(*result.SourceManager, range, replacement)); | |
270 } | |
271 | |
272 void ProxyGetterCallback::run(const MatchFinder::MatchResult& result) { | |
273 const CXXMemberCallExpr* call = | |
274 result.Nodes.getNodeAs<CXXMemberCallExpr>("call"); | |
275 const MemberExpr* callee = clang::dyn_cast<MemberExpr>(call->getCallee()); | |
276 clang::CharSourceRange range = | |
277 clang::CharSourceRange::getTokenRange(callee->getSourceRange()); | |
278 | |
279 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
280 result.Context->getLangOpts()); | |
281 | |
282 text = ReplaceFirst(text, "message_loop_proxy", "task_runner"); | |
283 replacements_->insert(Replacement(*result.SourceManager, range, text)); | |
284 } | |
285 | |
286 void ProxyVariableCallback::run(const MatchFinder::MatchResult& result) { | |
287 const VarDecl* var = result.Nodes.getNodeAs<VarDecl>("var"); | |
288 clang::CharSourceRange range = | |
289 clang::CharSourceRange::getTokenRange(var->getSourceRange()); | |
290 | |
291 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
292 result.Context->getLangOpts()); | |
293 | |
294 text = ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner"); | |
295 replacements_->insert(Replacement(*result.SourceManager, range, text)); | |
296 } | |
297 | |
298 void RefPtrProxyVariableCallback::run(const MatchFinder::MatchResult& result) { | |
299 const VarDecl* var = result.Nodes.getNodeAs<VarDecl>("var"); | |
300 clang::CharSourceRange range = | |
301 clang::CharSourceRange::getTokenRange(var->getSourceRange()); | |
302 | |
303 std::string text = clang::Lexer::getSourceText(range, *result.SourceManager, | |
304 result.Context->getLangOpts()); | |
305 | |
306 text = ReplaceFirst(text, "MessageLoopProxy", "SingleThreadTaskRunner"); | |
307 replacements_->insert(Replacement(*result.SourceManager, range, text)); | |
308 } | |
309 | |
310 } // namespace | |
311 | |
312 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | |
313 | |
314 int main(int argc, const char* argv[]) { | |
315 llvm::cl::OptionCategory category("MessageLoop refactoring tool"); | |
316 CommonOptionsParser options(argc, argv, category); | |
317 clang::tooling::ClangTool tool(options.getCompilations(), | |
318 options.getSourcePathList()); | |
319 | |
320 Replacements replacements; | |
321 MessageLoopRefactorer refactorer(&replacements); | |
322 MatchFinder match_finder; | |
323 refactorer.SetupMatchers(&match_finder); | |
324 | |
325 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = | |
326 clang::tooling::newFrontendActionFactory(&match_finder); | |
327 int result = tool.run(frontend_factory.get()); | |
328 if (result != 0) | |
329 return result; | |
330 | |
331 // Each replacement line should have the following format: | |
332 // r:<file path>:<offset>:<length>:<replacement text> | |
333 // Only the <replacement text> field can contain embedded ":" characters. | |
334 // TODO(dcheng): Use a more clever serialization. Ideally we'd use the YAML | |
335 // serialization and then use clang-apply-replacements, but that would require | |
336 // copying and pasting a larger amount of boilerplate for all Chrome clang | |
337 // tools. | |
338 llvm::outs() << "==== BEGIN EDITS ====\n"; | |
339 for (const auto& r : replacements) { | |
340 llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() | |
341 << ":::" << r.getLength() << ":::" << r.getReplacementText() | |
342 << "\n"; | |
343 } | |
344 llvm::outs() << "==== END EDITS ====\n"; | |
345 | |
346 return 0; | |
347 } | |
OLD | NEW |