| Index: tools/clang/BindMigrate/OldCallbackTransform.cpp
|
| diff --git a/tools/clang/BindMigrate/OldCallbackTransform.cpp b/tools/clang/BindMigrate/OldCallbackTransform.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..92eac98f9770953049519c7ebf25925148f8213f
|
| --- /dev/null
|
| +++ b/tools/clang/BindMigrate/OldCallbackTransform.cpp
|
| @@ -0,0 +1,417 @@
|
| +// 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 "OldCallbackTransform.h"
|
| +
|
| +#include "clang/AST/ASTContext.h"
|
| +#include "clang/AST/Expr.h"
|
| +#include "clang/AST/ExprCXX.h"
|
| +#include "clang/Basic/SourceManager.h"
|
| +#include "clang/Rewrite/Rewriter.h"
|
| +
|
| +#include "DiagnosticEmitter.h"
|
| +
|
| +using namespace std;
|
| +
|
| +namespace {
|
| +
|
| +std::string RemoveSpaceBeforePtrOrRefInType(const std::string &orig) {
|
| + if (orig.size() < 2) {
|
| + return orig;
|
| + }
|
| +
|
| + string out;
|
| + std::string::const_iterator it = orig.begin();
|
| + std::string::const_iterator end = orig.end();
|
| + char prev_char = *(it++);
|
| + for (; it != end; ++it) {
|
| + // Since it's a type, we know it's not going to be a binary expression, so
|
| + // we can just look for the single chars and deicde if we should skip the
|
| + // last char.
|
| + if (*it != '*' && *it != '&') {
|
| + out.append(1, prev_char);
|
| + }
|
| +
|
| + prev_char = *it;
|
| + }
|
| +
|
| + out.append(1, prev_char);
|
| + return out;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +namespace clang {
|
| +
|
| +OldCallbackTransform::OldCallbackTransform(ASTContext* context,
|
| + Rewriter* rewriter,
|
| + DiagnosticEmitter* emitter)
|
| + : context_(context), rewriter_(rewriter), error_emitter_(emitter) {
|
| +}
|
| +
|
| +bool OldCallbackTransform::WouldTransformStmt(Stmt* statement) {
|
| + return IsOldCallbackGenerator(statement) || IsOldCallbackRun(statement);
|
| +}
|
| +
|
| +bool OldCallbackTransform::WouldTransformDecl(Decl* decl) {
|
| + bool dont_care = false;
|
| + if (TypedefDecl* td = dyn_cast_or_null<TypedefDecl>(decl)) {
|
| + return IsOldCallbackType(td->getUnderlyingType(), &dont_care);
|
| + } else if (VarDecl* vd = dyn_cast_or_null<VarDecl>(decl)) {
|
| + return IsOldCallbackType(vd->getType(), &dont_care);
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +void OldCallbackTransform::TransformStmt(Stmt *statement) {
|
| + // Catch all NewCallback() calls.
|
| + RewriteIfOldCallbackGenerator(statement);
|
| +
|
| + // Catch all CallbackN::Run() calls.
|
| + RewriteIfOldCallbackRun(statement);
|
| +}
|
| +
|
| +void OldCallbackTransform::TransformDecl(Decl *decl) {
|
| + // Catch all typedefs and variables/parameter declarations.
|
| + if (TypedefDecl* td = dyn_cast_or_null<TypedefDecl>(decl)) {
|
| + RewriteTypedefIfOldCallback(td);
|
| + } else if (VarDecl* vd = dyn_cast_or_null<VarDecl>(decl)) {
|
| + RewriteVarIfOldCallback(vd);
|
| + }
|
| +}
|
| +
|
| +void OldCallbackTransform::RewriteIfOldCallbackGenerator(Stmt* statement) {
|
| + if (!IsOldCallbackGenerator(statement)) {
|
| + return;
|
| + }
|
| +
|
| + // Both callback variants only work with methods. So we always s
|
| + // NewCallback* -> base::Bind
|
| + CallExpr* ce = dyn_cast<CallExpr>(statement);
|
| + if (rewriter_->ReplaceText(ce->getCallee()->getSourceRange(),
|
| + "base::Bind")) {
|
| + error_emitter_->EmitError(statement->getLocStart(),
|
| + "Unable to rewrite to base::Bind()");
|
| + }
|
| +
|
| + Expr* arg1 = ce->getArgs()[0];
|
| + Expr* arg2 = ce->getArgs()[1];
|
| +
|
| + if (rewriter_->ReplaceStmt(arg1, arg2)) {
|
| + error_emitter_->EmitError(statement->getLocStart(),
|
| + "Unable to swap the first argument");
|
| + }
|
| +
|
| + std::string unretained_arg("base::Unretained(");
|
| + unretained_arg += rewriter_->ConvertToString(arg1);
|
| + unretained_arg += ")";
|
| + if (rewriter_->ReplaceText(arg2->getSourceRange(), unretained_arg)) {
|
| + error_emitter_->EmitError(statement->getLocStart(),
|
| + "Unable to rewrite the second argument");
|
| + }
|
| +}
|
| +
|
| +void OldCallbackTransform::RewriteIfOldCallbackRun(Stmt* statement) {
|
| + if (!IsOldCallbackRun(statement)) {
|
| + return;
|
| + }
|
| +
|
| + // Grab the statement and do "s/->Run(/.Run(/".
|
| + SourceManager& source_manager = context_->getSourceManager();
|
| + std::pair<FileID,unsigned> start = source_manager.getDecomposedLoc(
|
| + statement->getLocStart());
|
| + std::pair<FileID,unsigned> end = source_manager.getDecomposedLoc(
|
| + statement->getLocEnd());
|
| +
|
| + StringRef buffer = source_manager.getBufferData(start.first);
|
| + static const char* kOldRun = "->Run(";
|
| + size_t where = buffer.find(kOldRun, start.second);
|
| +
|
| +
|
| + if (where > end.second) {
|
| + // There's no ".Run(" in this statement. Lucky us. Exit early.
|
| + return;
|
| + }
|
| +
|
| + SourceLocation replace_start =
|
| + source_manager.getLocForStartOfFile(start.first)
|
| + .getFileLocWithOffset(where);
|
| + std::pair<FileID,unsigned> rstart_b = source_manager.getDecomposedLoc(
|
| + replace_start);
|
| +
|
| + if (rewriter_->ReplaceText(replace_start, strlen(kOldRun), ".Run(")) {
|
| + error_emitter_->EmitWarning(replace_start,
|
| + "Unable to rewrite ->Run() -> .Run()");
|
| + }
|
| +}
|
| +
|
| +void OldCallbackTransform::RewriteTypedefIfOldCallback(TypedefDecl* td) {
|
| + // Skip anything from callback_old.h. That'd be silly.
|
| + // TODO(ajwong): do this.
|
| +
|
| + bool is_callback_with_return = false;
|
| + QualType underlying_type = td->getUnderlyingType();
|
| + if (!IsOldCallbackType(underlying_type, &is_callback_with_return)) {
|
| + return;
|
| + }
|
| +
|
| + string new_typedef = "typedef ";
|
| +
|
| + if (!CallbackFromType(underlying_type, td->getLocStart(),
|
| + is_callback_with_return, &new_typedef)) {
|
| + // Error emited by CallbackFromType function.
|
| + return;
|
| + }
|
| +
|
| + if (underlying_type->isReferenceType()) {
|
| + error_emitter_->EmitWarning(
|
| + td->getLocStart(),
|
| + "Template to a reference type? That's strange. Skipping.");
|
| + return;
|
| + }
|
| +
|
| + // Add identifier
|
| + new_typedef += " ";
|
| + new_typedef += td->getIdentifier()->getName();
|
| + new_typedef += ";";
|
| +
|
| + // ?!?!?!?!! WTF DOES THIS RETURN TRUE ON ERROR?!
|
| + if (rewriter_->ReplaceText(td->getSourceRange(), new_typedef)) {
|
| + error_emitter_->EmitError(td->getLocStart(), "Unable to rewrite typedef.");
|
| + }
|
| +}
|
| +
|
| +void OldCallbackTransform::RewriteVarIfOldCallback(VarDecl* vd) {
|
| + bool is_callback_with_return = false;
|
| + if (!IsOldCallbackType(vd->getType(), &is_callback_with_return)) {
|
| + return;
|
| + }
|
| +
|
| + std::string new_decl;
|
| + if (!CallbackFromType(vd->getType(), vd->getLocStart(),
|
| + is_callback_with_return, &new_decl)) {
|
| + // Error emited by CallbackFromType function.
|
| + return;
|
| + }
|
| +
|
| + new_decl += " ";
|
| + new_decl += vd->getIdentifier()->getName();
|
| + if (rewriter_->ReplaceText(vd->getSourceRange(), new_decl)) {
|
| + error_emitter_->EmitError(vd->getLocStart(), "Unable to rewrite var.");
|
| + }
|
| +}
|
| +
|
| +bool OldCallbackTransform::CallbackFromType(QualType type,
|
| + SourceLocation loc,
|
| + bool is_callback_with_return,
|
| + std::string* output) {
|
| + string buffer;
|
| +
|
| + // Rewrite the whole declaration including const, and volatile. Start
|
| + // constructing here. Make sure to not propagate pointers though.
|
| + if (type.isConstQualified()) buffer += "const ";
|
| + if (type.isRestrictQualified()) buffer += "restrict ";
|
| + if (type.isVolatileQualified()) buffer += "volatile ";
|
| +
|
| + if (is_callback_with_return) {
|
| + if (!CallbackFromTypeWithReturn(type, loc, &buffer)) {
|
| + return false;
|
| + }
|
| + } else {
|
| + if (!CallbackFromVoidReturnType(type, loc, &buffer)) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + if (type->isReferenceType()) buffer += "&";
|
| +
|
| + output->append(buffer);
|
| + return true;
|
| +}
|
| +
|
| +bool OldCallbackTransform::CallbackFromVoidReturnType(QualType type,
|
| + SourceLocation loc,
|
| + std::string* output) {
|
| + // Grab record.
|
| + ClassTemplateSpecializationDecl* callback_decl;
|
| + if (type->isPointerType()) {
|
| + callback_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
|
| + type->getPointeeType()->getAsCXXRecordDecl());
|
| + } else {
|
| + callback_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
|
| + type.getNonReferenceType()->getAsCXXRecordDecl());
|
| + }
|
| +
|
| + // This wasn't a specialization? Huh?
|
| + if (!callback_decl) {
|
| + error_emitter_->EmitWarning(
|
| + loc,
|
| + "Skipping Callback* Identifier doesn't refer to class or template.");
|
| + return false;
|
| + }
|
| +
|
| + // Expect only one argument in the runner. That argument will be a Tuple type.
|
| + const TemplateArgumentList& template_args = callback_decl->getTemplateArgs();
|
| + if (template_args.size() != 1) {
|
| + error_emitter_->EmitWarning(
|
| + loc,
|
| + "Skipping Callback*. "
|
| + "Expected to find TupleN<> in Runner specialization.");
|
| + return false;
|
| + }
|
| +
|
| + CXXRecordDecl* runner_specialization =
|
| + template_args[0].getAsType()->getAsCXXRecordDecl();
|
| +
|
| + if (!runner_specialization ||
|
| + runner_specialization->getNameAsString().find("Tuple") != 0) {
|
| + error_emitter_->EmitWarning(
|
| + loc,
|
| + "Skipping Callback*. "
|
| + "Expected to find TupleN<> in Runner specialization.");
|
| + return false;
|
| + }
|
| +
|
| + ClassTemplateSpecializationDecl* tuple_decl =
|
| + dyn_cast<ClassTemplateSpecializationDecl>(runner_specialization);
|
| + if (!tuple_decl &&
|
| + runner_specialization->getNameAsString() != "Tuple0") {
|
| + error_emitter_->EmitWarning(loc, "Skipping Callback*. Expected Tuple0.");
|
| + return false;
|
| + }
|
| +
|
| + if (!tuple_decl) {
|
| + *output += "base::Closure";
|
| + } else {
|
| + ConvertTupleType(tuple_decl->getTemplateArgs(), output);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool OldCallbackTransform::CallbackFromTypeWithReturn(QualType type,
|
| + SourceLocation loc,
|
| + std::string* output) {
|
| + // We know we're a CallbackFromTypeWithReturn<>::Type value now so just dig
|
| + // out the info.
|
| + CXXRecordDecl* callback_decl;
|
| + if (type->isPointerType()) {
|
| + callback_decl = type->getPointeeType()->getAsCXXRecordDecl();
|
| + } else {
|
| + callback_decl = type.getNonReferenceType()->getAsCXXRecordDecl();
|
| + }
|
| +
|
| + ClassTemplateSpecializationDecl* specialization_decl =
|
| + dyn_cast<ClassTemplateSpecializationDecl>(callback_decl->getParent());
|
| +
|
| + string buffer;
|
| + llvm::raw_string_ostream output_os(buffer);
|
| + output_os << "base::Callback<";
|
| + specialization_decl->getTemplateArgs()[0].print(context_->PrintingPolicy,
|
| + output_os);
|
| + output_os << "(void)>";
|
| +
|
| + output_os.flush();
|
| + output->append(RemoveSpaceBeforePtrOrRefInType(buffer));
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool OldCallbackTransform::IsOldCallbackType(QualType type,
|
| + bool* is_callback_with_return) {
|
| +
|
| + // First off, is this thing class, or a poitner to a class?
|
| + QualType bare_type = type.getNonReferenceType();
|
| + if (bare_type->isPointerType()) {
|
| + bare_type = bare_type->getPointeeType();
|
| + }
|
| + const CXXRecordDecl* type_decl = bare_type->getCXXRecordDeclForPointerType();
|
| + if (!type_decl) type_decl = bare_type->getAsCXXRecordDecl();
|
| + if (!type_decl) return false;
|
| + type_decl = type_decl->getCanonicalDecl();
|
| +
|
| + // Yay. A class. How quiant. Okay "class," what's your name?
|
| + string name = type_decl->getNameAsString();
|
| +
|
| + // A CallbackRunner huh? Well, aren't you special?
|
| + if (name == "CallbackRunner") return true;
|
| +
|
| + // We don't normally accept Types, but when we do, we accept
|
| + // CallbackWithReturnValue::Types.
|
| + if (name == "Type") {
|
| + if (const CXXRecordDecl* encloding_class =
|
| + dyn_cast_or_null<CXXRecordDecl>(type_decl->getParent())) {
|
| + if (encloding_class->getNameAsString() == "CallbackWithReturnValue") {
|
| + *is_callback_with_return = true;
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool OldCallbackTransform::IsOldCallbackGenerator(Stmt* statement) {
|
| + CallExpr* ce = dyn_cast_or_null<CallExpr>(statement);
|
| + if (!ce) return false;
|
| +
|
| + FunctionDecl *fd = ce->getDirectCallee();
|
| + if (!fd) return false;
|
| +
|
| + std::string function_name = fd->getNameInfo().getName().getAsString();
|
| + if ("NewCallback" != function_name &&
|
| + "NewCallbackWithReturnValue" != function_name) {
|
| + return false;
|
| + }
|
| +
|
| + if (ce->getNumArgs() < 2) {
|
| + error_emitter_->EmitWarning(
|
| + statement->getLocStart(),
|
| + "NewCallback.* with less than 2 args?! Inconceivable!");
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool OldCallbackTransform::IsOldCallbackRun(Stmt* statement) {
|
| + bool is_callback_with_return = false;
|
| + CallExpr* ce = dyn_cast_or_null<CallExpr>(statement);
|
| + if (!ce) return false;
|
| +
|
| + CXXMethodDecl *md = dyn_cast_or_null<CXXMethodDecl>(ce->getDirectCallee());
|
| + if (!md) return false;
|
| +
|
| + std::string function_name = md->getNameInfo().getName().getAsString();
|
| + if ("Run" != function_name) return false;
|
| +
|
| + if (!IsOldCallbackType(
|
| + md->getParent()->getTypeForDecl()->getCanonicalTypeUnqualified(),
|
| + &is_callback_with_return)) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void OldCallbackTransform::ConvertTupleType(
|
| + const TemplateArgumentList& template_args,
|
| + std::string* output) {
|
| + string buffer;
|
| + llvm::raw_string_ostream output_os(buffer);
|
| + output_os << "base::Callback<void(";
|
| + for (size_t i = 0; i < template_args.size(); ++i) {
|
| + template_args[i].print(context_->PrintingPolicy, output_os);
|
| + if (i != (template_args.size() - 1)) {
|
| + output_os << ", ";
|
| + }
|
| + }
|
| + output_os << ")>";
|
| +
|
| + output_os.flush();
|
| + output->append(RemoveSpaceBeforePtrOrRefInType(buffer));
|
| +}
|
| +
|
| +} // namespace clang
|
|
|