百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

基于Clang库实现C++文件修改 c++ docx库

liebian365 2024-11-12 13:07 27 浏览 0 评论



Clang通过两种方式访问AST(抽象语法树)

  • Visitor遍历模式
  • Matcher匹配模式

Visitor遍历模式


初始化CompilerInstance之后,调用其成员函数ExcutionAction, ExcutionAction会间接依次调用FrontendAction的6个成员函数(直接调用的是FrontendAction的三个public 接口,BeginSourceFile,Execute,EndSourceFile),而FrontendAction的ExecuteAction会最终调用语法分析函数ParseAST(未强制要求ParseAST放入ExcuteAction,但ASTFrontendAction如此)。 ParseAST在分析过程中,又会调用ASTConsumer的多个函数(用得最多是HandleTopLevelDecl和 HandleTranslationUnit)。FrontendAction是文件级别的动作,ASTConsumer则与一个Translation Unit内部处理过程相关。RecursiveASTVisitor是针对AST node的遍历,一般需要ASTConsumer中呈现的AST node(如TranslationUnitDecl)作为参数。  

深度优先的方式遍历整个 Clang AST 并访问每个节点的一个类。其执行三种不同的操作:

  1. 遍历整个 AST(即访问每个节点)
  2. 对于给定的一个节点,沿着其类继承关系向前(Derived->Base方向)游历,直至到达一个顶层类(如 Stmt,Decl,Type)
  3. 对于一个给定的 (node, class) 组合,调用一个可由用户重写(user-overridable)的函数来访问该节点,其中 class 是 node 的动态类型的某个基类

这些操作由三组类方法来完成,分别是:

  1. TraverseDecl(Decl *x) 执行任务1,是遍历以x为根的 AST 的入口函数。该函数只是简单地将任务分派给 TraverseFoo(Foo *x),继而调用 WalkUpFromFoo(x),然后递归地访问x的子节点。TraverseFoo 中的 Foo 是 *x 的动态类型。TraverseDecl(Decl *x) 和 TraverseType(QualType x) 执行的操作类似
  2. WalkUpFromFoo(Foo *x) 执行任务2。该函数首先调用 WalkUpFromBar(x),然后调用 VisitFoo(x)。其中 Bar 是 Foo 的直接父类(direct parent)
  3. VisitFoo(Foo *x) 执行任务3

这三组方法具有下列层次关系:Traverse* > WalkUpFrom* > Visit*。某一层次的方法可以调用相同层次的另一个方法以及较低层次的方法,但不能调用层次比它高的方法。


Matcher匹配模式

ifStmt(hasCondition(expr().bind("C")), hasThen(stmt().bind("T")), hasElse(stmt().bind("E")))
  • ifStmt是一个匹配器实例,用来判断匹配当前节点是否为 if 节点,并且满足括号内的其他匹配器要求。他是一个包含 operator() 方法的类的实例,但是你也可以将其简单地看作是一个函数
  • expr 和 stmt:与 ifStmt 类似,也是匹配器的实例,用于匹配 Expression 和 Statement。
  • .bind(str):一些匹配器拥有的方法,它可以将被匹配的这个子表达式树绑定到 str 中。例如上文中,匹配成功后会将绑定到 C 上,我们后续可以通过 C 找到这个表达式。
  • hasConditon、hasThen、hasElse:它是一个作用于特定节点类型的匹配器,它尝试去获得节点的子树。


在 Clang 中,AST Matcher 被分为了三类:

  1. 节点匹配器(Node Matchers):用于匹配特定类型的节点,是匹配器的核心。同时,Node Matchers 是唯一具有 bind 方法的一类匹配器。例如 ifStmt、expr、classTemplateDecl 等。
  2. 窄化匹配器(Narrowing Matchers):对当前节点上的某些属性进行匹配,从而缩小当前类型的节点集的匹配范围。例如 isPrivate、equals、isConst 等。此外,有一些逻辑匹配器(allOf、anyOf、anything、unless),他们能构成更强大的匹配器。
  3. 遍历匹配器(Traversal Matchers):遍历匹配器指定从当前节点可到达的其他节点的关系。例如 hasCondition、hasParent、hasLHS 等。此外,有一些强大的匹配器 (has、hasDecendant、forEach)可以构成更强大的匹配器。

每一个匹配表达式都以 Node Matchers 开始,然后使用 Narrowing 或 Traversal Matcher 进一步完善。所有遍历匹配器都将 Node Matcher 作为其参数。此外,每个 Node Matcher 都隐含了一个 allOf Matcher。

实现

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
#include "clang/Driver/Options.h"
#include "clang/Rewrite/Frontend/FixItRewriter.h"
#include "clang/Rewrite/Frontend/FrontendActions.h"
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Syntax/BuildTree.h"
#include "clang/Tooling/Syntax/Tokens.h"
#include "clang/Tooling/Syntax/Tree.h"
#include "clang/Tooling/Tooling.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"

using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::driver;
using namespace clang::tooling;
using namespace llvm;


static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
static cl::extrahelp MoreHelp(
    "\tFor example, to run rewriter\n"
    "\n");

static cl::OptionCategory ClangCheckCategory("rewriter options");
static const opt::OptTable &Options = getDriverOptTable();
static cl::opt<bool>
    VISITORDump("visitor",
            cl::desc(Options.getOptionHelpText(options::OPT_ast_dump)),
            cl::cat(ClangCheckCategory));


// Visitor 遍历模式
class ReWriteClassVisitor
    : public RecursiveASTVisitor<ReWriteClassVisitor> {
public:
  explicit ReWriteClassVisitor(ASTContext *context, SourceManager *sm, Rewriter *rw)
      : context_(context), sm_(sm), rewriter_(rw) {}

    bool VisitStmt(Stmt *s) {
        // 当前文件,排除包含文件
      if (sm_->isInMainFile(s->getBeginLoc()) && isa<IfStmt>(s)) {
            IfStmt *IfStatement = cast<IfStmt>(s);
            Stmt *Then = IfStatement->getThen();
            rewriter_->InsertText(Then->getBeginLoc(), "// visitor, the 'if' part\n", true, true);
            Stmt *Else = IfStatement->getElse();
            if (Else) {
              rewriter_->InsertText(Else->getBeginLoc(), "// visitor, the 'else' part\n", true, true);
            }
        }
      return true;
    }

private:
  ASTContext *context_;
  SourceManager *sm_;
  std::string caller_;
  Rewriter *rewriter_;
};


// Matcher 匹配模式
class IfStmtHandler : public MatchFinder::MatchCallback {
public:
  IfStmtHandler(Rewriter &Rewrite) : Rewrite(Rewrite) {}

  virtual void run(const MatchFinder::MatchResult &Result) {
    if (const IfStmt *IfS = Result.Nodes.getNodeAs<clang::IfStmt>("ifStmt")) {
      const Stmt *Then = IfS->getThen();
      Rewrite.InsertText(Then->getBeginLoc(), "// matcher, the 'if' part\n", true, true);

      if (const Stmt *Else = IfS->getElse()) {
        Rewrite.InsertText(Else->getBeginLoc(), "// matcher the 'else' part\n", true, true);
      }
    }
  }
private:
  Rewriter &Rewrite;
};

class ReWriteClassConsumer : public clang::ASTConsumer {
public:
  explicit ReWriteClassConsumer(ASTContext *context, SourceManager *sm,
                                  Rewriter *rw)
      : visitor_(context, sm, rw), sm_(sm), rewriter_(rw), handlerif_(*rw) {}

  ~ReWriteClassConsumer() {
    llvm::outs() << "I have finished rewrite." << "\n";

  }
  virtual void Initialize(ASTContext &Context) override {
    context_ = &Context;
    matcher_.addMatcher(ifStmt().bind("ifStmt"), &handlerif_);

  }


  virtual void HandleTranslationUnit(clang::ASTContext &context) override {

    if (VISITORDump) {
      // 使用遍历模式
      visitor_.TraverseDecl(context.getTranslationUnitDecl());
    } else {
      // 使用匹配模式
      matcher_.matchAST(context);
    }
  }

private:
  ReWriteClassVisitor visitor_;
  ASTContext *context_;
  SourceManager *sm_;
  Rewriter *rewriter_;
  IfStmtHandler handlerif_;
  MatchFinder matcher_;
};

class ReWriteClassAction : public clang::ASTFrontendAction {
public:
  void EndSourceFileAction() override {
    //rewriter_.getEditBuffer(rewriter_.getSourceMgr().getMainFileID()) .write(llvm::outs());
    rewriter_.overwriteChangedFiles();
  }


  virtual std::unique_ptr<clang::ASTConsumer>
  CreateASTConsumer(clang::CompilerInstance &compiler, llvm::StringRef in_file) {
    // 屏蔽错误信息输出
    compiler.getDiagnostics().setClient(new IgnoringDiagConsumer()); // 相当于-w
    rewriter_.setSourceMgr(compiler.getSourceManager(), compiler.getLangOpts());
    return std::make_unique<ReWriteClassConsumer>(
        &compiler.getASTContext(), &compiler.getSourceManager(), &rewriter_);
  }

private:
  Rewriter rewriter_;
  };


//命令行参数: ast.cpp --  屏蔽编译数据库找不到
int main(int argc, const char **argv) {
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);

  // Initialize targets for clang module support.
  llvm::InitializeAllTargets();
  llvm::InitializeAllTargetMCs();
  llvm::InitializeAllAsmPrinters();
  llvm::InitializeAllAsmParsers();

  auto ExpectedParser =
      CommonOptionsParser::create(argc, argv, ClangCheckCategory);
  if (!ExpectedParser) {
    llvm::errs() << ExpectedParser.takeError();
    return 1;
  }
  CommonOptionsParser &OptionsParser = ExpectedParser.get();
  ClangTool Tool(OptionsParser.getCompilations(),
                 OptionsParser.getSourcePathList());

  std::unique_ptr<FrontendActionFactory> FrontendFactory;

  FrontendFactory = newFrontendActionFactory<ReWriteClassAction>();

  return Tool.run(FrontendFactory.get());
}


验证

文件:ast.cpp

//for linux,__cdecl
// g++ -g test_objetc_member_function_linux.cpp -std=c++11 -I../src -o test_objetc_member_function_linux
#include<iostream>
#include "stub.h"
using namespace std;

template<typename T>
void swap(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
}

class A{
    int i;
public:
    int foo(int a){
        if(a > 1)
            cout<<"I am A_foo:"<< a <<endl;
        else
            cout<<"I am A_foo > 1:"<< a <<endl;
        return 0;
    }
    static int bar(int b){
        cout<<"I am A_bar:"<< b <<endl;
        return 0;        
        
    }
};

int foo_stub(void* obj, int a)
{   
    A* o= (A*)obj;
    cout<<"I am foo_stub"<<endl;
    return 0;
}

int main()
{
    Stub stub;
    stub.set(ADDR(A,foo), foo_stub);
    A a;
    a.foo(1);
    int aa = 1, bb=2;
    swap(aa, bb);
    cout<<aa<<bb<<endl;
    
    return 0;
}

执行:rewriter -visitor ast.cpp --

结果:



再执行:rewriter ast.cpp --

结果:

相关推荐

4万多吨豪华游轮遇险 竟是因为这个原因……

(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...

“菜鸟黑客”必用兵器之“渗透测试篇二”

"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...

科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白

作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...

麦子陪你做作业(二):KEGG通路数据库的正确打开姿势

作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...

知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势

智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...

每日新闻播报(September 14)_每日新闻播报英文

AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...

香港新巴城巴开放实时到站数据 供科技界研发使用

中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...

5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper

本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...

Qt动画效果展示_qt显示图片

今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...

如何从0到1设计实现一门自己的脚本语言

作者:dong...

三年级语文上册 仿写句子 需要的直接下载打印吧

描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...

C++|那些一看就很简洁、优雅、经典的小代码段

目录0等概率随机洗牌:1大小写转换2字符串复制...

二年级上册语文必考句子仿写,家长打印,孩子照着练

二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...

一年级语文上 句子专项练习(可打印)

...

亲自上阵!C++ 大佬深度“剧透”:C++26 将如何在代码生成上对抗 Rust?

...

取消回复欢迎 发表评论: