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

Unified 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, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/clang/BindMigrate/PostTaskTransform.h ('k') | tools/clang/BindMigrate/README.chromium » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/clang/BindMigrate/PostTaskTransform.cpp
diff --git a/tools/clang/BindMigrate/PostTaskTransform.cpp b/tools/clang/BindMigrate/PostTaskTransform.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c5680243c9bfcb7cd071092ed56bfb95443f6ebd
--- /dev/null
+++ b/tools/clang/BindMigrate/PostTaskTransform.cpp
@@ -0,0 +1,152 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "PostTaskTransform.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Rewrite/Rewriter.h"
+
+#include "DiagnosticEmitter.h"
+
+using namespace std;
+
+namespace clang {
+
+PostTaskTransform::PostTaskTransform(ASTContext* context, Rewriter* rewriter,
+ DiagnosticEmitter* emitter)
+ : context_(context), rewriter_(rewriter), error_emitter_(emitter) {
+ interesting_classes_.insert("MessageLoop");
+ interesting_classes_.insert("MessageLoopProxy");
+}
+
+bool PostTaskTransform::WouldTransformStmt(Stmt* statement) {
+ return IsPostTaskNRMExpr(statement);
+}
+
+void PostTaskTransform::TransformStmt(Stmt* statement) {
+ if (!IsPostTaskNRMExpr(statement)) return;
+
+ // Catch the MessageLoop and MessageLoopProxy calls.
+ CXXMemberCallExpr* mce = dyn_cast<CXXMemberCallExpr>(statement);
+ RewriteIfNewRunnableSomething(mce->getArgs()[1]);
+}
+
+bool PostTaskTransform::IsPostTaskNRMExpr(Stmt* statement) {
+ CXXMemberCallExpr* call = dyn_cast_or_null<CXXMemberCallExpr>(statement);
+ if (!call) return false;
+ CXXMethodDecl* method_decl = call->getMethodDecl();
+ if (!method_decl) return false;
+ method_decl = method_decl->getCanonicalDecl();
+ if ("PostTask" != method_decl->getNameAsString()) {
+ return false;
+ }
+
+ string classname = method_decl->getThisType(*context_)
+ .getBaseTypeIdentifier()->getName();
+
+ if (interesting_classes_.find(classname) == interesting_classes_.end())
+ return false;
+
+ if (call->getNumArgs() < 2) {
+ error_emitter_->EmitError(
+ statement->getLocStart(),
+ "PostTask with less than 2 args?! Inconceivable!");
+ return false;
+ }
+
+ // Check that PostTask second arg is actually the ::NewRunnableMethod() call.
+ CallExpr* nrm_ce = dyn_cast<CallExpr>(call->getArgs()[1]);
+ if (!nrm_ce) return false;
+ if (dyn_cast<CXXMemberCallExpr>(nrm_ce)) return false;
+ FunctionDecl *nrm_fd = nrm_ce->getDirectCallee();
+ if (!nrm_fd) return false;
+ std::string nrm_function_name =
+ nrm_fd->getNameInfo().getName().getAsIdentifierInfo()->getName();
+ if ("NewRunnableMethod" != nrm_function_name) {
+ return false;
+ }
+
+ if (nrm_ce->getNumArgs() < 2) {
+ error_emitter_->EmitWarning(
+ nrm_ce->getLocStart(),
+ "NewRunnableMethod with less than 2 args?! Inconceivable!");
+ return false;
+ }
+
+ return true;
+}
+
+static bool NoAddrefRelease(const CXXRecordDecl *record, void *user_data) {
+ bool found_addref = false;
+ bool found_release = false;
+
+ for (CXXRecordDecl::method_iterator it = record->method_begin(),
+ end = record->method_end(); it != end; ++it) {
+ if (it->getAccess() == AS_public) {
+ found_addref |= "AddRef" == it->getNameAsString();
+ found_release |= "Release" == it->getNameAsString();
+ }
+ }
+
+ return !(found_addref && found_release);
+}
+
+bool PostTaskTransform::IsRefcountedType(QualType type) {
+ CXXRecordDecl* record = type->getAsCXXRecordDecl();
+ return !(NoAddrefRelease(record, NULL) &&
+ record->forallBases(&NoAddrefRelease, NULL, true));
+}
+
+bool PostTaskTransform::RewriteIfNewRunnableSomething(Expr* post_task_arg) {
+ // Skip any implicit conversions and any casts. We wantz the function!!
+ post_task_arg = post_task_arg->IgnoreImplicit()->IgnoreParenCasts();
+
+ CallExpr* ce = dyn_cast<CallExpr>(post_task_arg);
+ if (!ce) return false;
+ FunctionDecl *fd = ce->getDirectCallee();
+ if (!fd) return false;
+
+ // Okay, here's where it gets fun. We need to
+ // (1) replace the NRM identifier text with base::Bind.
+ // (2) Swap positions of the first two arguments.
+ // (3) If we're calling a method, and the class is not refcounted, then
+ // a DISABLE_RUNNABLE_METHOD_REFCOUNT or similar must have been used
+ // to disable refcounting. Insert a base::Unretained().
+
+ // NewRunnableMethod -> base::Bind
+ bool failure = rewriter_->ReplaceText(
+ ce->getCallee()->getSourceRange(), "base::Bind");
+
+ Expr* arg1 = ce->getArgs()[0];
+ Expr* arg2 = ce->getArgs()[1];
+
+
+ // Swap args, and add unretained to the object if needed.
+ failure |= rewriter_->ReplaceStmt(arg1, arg2);
+ if (arg2->getType()->isMemberFunctionPointerType() &&
+ arg1->getType()->isPointerType() &&
+ arg1->getType()->getPointeeType()->isStructureOrClassType() &&
+ !IsRefcountedType(arg1->getType()->getPointeeType())) {
+ // The rewriter works in the context of the soruce buffer. Though it
+ // tries to be smart about mapping the source location into the
+ // destination over multiple rewrites, it can get confused if you try
+ // to modify the same region of text multiple times. Doing things in
+ // order from "left to right" seems to keep the Rewritter happier.
+ //
+ // TODO(ajwong): See if the ARCMigrate source transaction system that
+ // batches and combines modificaitons to be applied at the end can
+ // be generalized and reused here.
+ std::string unretained_arg("base::Unretained(");
+ unretained_arg += rewriter_->ConvertToString(arg1);
+ unretained_arg += ")";
+ failure |= rewriter_->ReplaceText(arg2->getSourceRange(), unretained_arg);
+ } else {
+ failure |= rewriter_->ReplaceStmt(arg2, arg1);
+ }
+ return failure;
+}
+
+} // namespace clang
« 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