| 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
|
|
|