代码异化项目解读


前言

实验室的代码异化项目,我来接手,但是有一说一,这个项目很复杂

头文件部分

consumer.h

主要就是定义了六中错误类型

include

这里面include了几个AST相关的东西:

#include <clang/AST/ASTConsumer.h>
#include <clang/AST/ASTContext.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/Rewrite/Core/Rewriter.h>
#include <cstdint>
#include <string>
#include <utility>
  1. <clang/AST/ASTConsumer.h>:该头文件提供了 ASTConsumer 类,它是一个抽象基类,定义了消耗 Clang 解析器生成的 AST(抽象语法树)的类的接口。
  2. <clang/AST/ASTContext.h>:该头文件提供了 ASTContext 类,表示 Clang 解析器生成的 AST 的上下文。
  3. <clang/ASTMatchers/ASTMatchFinder.h>:该头文件提供了 AST 匹配器和匹配器查找器,用于在 AST 中查找特定模式的节点。
  4. <clang/Rewrite/Core/Rewriter.h>:该头文件提供了 Rewriter 类,用于重写源代码,通常用于生成修改后的代码。

enum枚举部分

enum {
  // 语义错误
  OPERATOR_SEMANTIC = 0,//操作符错误
  BOUND_SEMANTIC,//边界错误
  VARUSE_SEMANTIC,//变量使用错误
  // 语法错误
  UNDEFVAR_SYNTAX,//未定义变量错误
  FORSEMI_SYNTAX,//for循环逗号错误
  PAREN_SYNTAX,//括号错误
  ALL_CONSUMER
};

父类:CustomASTConsumer

所有错误类型的父类,他本身也继承了clang::ASTConsumer

class CustomASTConsumer : public clang::ASTConsumer {
protected:
    // 用于查找 AST 中特定模式的 MatchFinder
    clang::ast_matchers::MatchFinder finder;

    // 存储 MatchCallback 的唯一指针的容器
    std::vector<std::unique_ptr<clang::ast_matchers::MatchFinder::MatchCallback>> callbacks;

public:
    // 纯虚函数,用于获取自定义 AST 消费者的名称
    virtual std::string name() const = 0;
};

callbacks 是一个容器,其中的每个元素都是一个指向 clang::ast_matchers::MatchFinder::MatchCallback 类型对象的 std::unique_ptr 智能指针。

OperatorSemantic

class OperatorSemantic : public CustomASTConsumer {
public:
  explicit OperatorSemantic(
      std::vector<std::function<void(clang::Rewriter &)>> &all);

  void HandleTranslationUnit(clang::ASTContext &Context) override;

  std::string name() const override;
};
explicit OperatorSemantic(
      std::vector<std::function<void(clang::Rewriter &)>> &all);

构造函数,接受一个vector,vector当中存储的是返回值为void,参数类型为Rewriter引用类型的函数

void HandleTranslationUnit(clang::ASTContext &Context) override;

处理翻译单元的AST

其余错误类型格式都是一样的,看一个就好了

ConsumerFactory

class ASTConsumerFactory {
public:
  static std::unique_ptr<clang::ASTConsumer>
  getASTConsumer(std::vector<std::function<void(clang::Rewriter &)>> &all,
                 int type = ALL_CONSUMER);

  static bool isValidType(int type) {
    return type < ALL_CONSUMER && type >= OPERATOR_SEMANTIC;
  }
};

用于获取 clang::ASTConsumer 的对象。它接受一个vector all 作为参数,该vector中的每个元素都是一个函数对象,这些函数对象接受一个 clang::Rewriter 的引用,并且返回 void。该函数还接受一个可选参数 type,默认值为 ALL_CONSUMER

util.h

就是用来生成随机数和打乱一个vector的

// 声明一个函数模板,用于生成指定长度的随机字符串
std::string randomStr(int n);

// 声明一个函数模板,用于随机打乱 vector 元素的顺序
template<typename T>
void shuffleVector(std::vector<T> &v) {
  // 初始化随机种子
  std::random_device randDevice;
  std::mt19937 generate(randDevice());

  // 随机排序
  shuffle(v.begin(), v.end(), generate);
}

在rand.cpp里会用到

exp.h

自定义了两种exp

#include <exception>

// 自定义异常类 InvalidConsumerExp,继承自 std::exception
class InvalidConsumerExp : public std::exception {
public:
    // 重写基类中的虚函数 what(),返回异常信息的 C 风格字符串
    const char *what() const noexcept override {
        return "Invalid Consumer Exception";
    }
};

// 自定义异常类 NoMoreConsumerExp,继承自 std::exception
class NoMoreConsumerExp : public std::exception {
public:
    // 重写基类中的虚函数 what(),返回异常信息的 C 风格字符串
    const char *what() const noexcept override {
        return "No More Consumer Exception";
    }
};

syntax包

主要是三种语法错误

ForSemiSyntax

生成错误的for语句,将for语句本来的分号替换为逗号

匿名namespace

namespace {

const string desc =
    "This bug is a syntax error where a comma is mistakenly used instead "
    "of a semicolon within a for loop";
const string id = "for";

class ForSemiCallback : public MatchFinder::MatchCallback {
public:
  vector<function<void(Rewriter &)>> &all;

  explicit ForSemiCallback(vector<function<void(Rewriter &)>> &all)
      : all(all) {}

  void run(const MatchFinder::MatchResult &result) override {
    const ForStmt *bo = result.Nodes.getNodeAs<ForStmt>(id);//获取循环语句的节点
    SourceManager *sm = result.SourceManager;
    if (bo == nullptr) return;

    // 获取初始子句的结束位置,这是第一个分号的位置
    const Stmt *init = bo->getInit();
    // 获取条件语句的结束位置, 这是第二个分号的位置
    const Stmt *cond = bo->getCond();

    if ((init || cond)&&sm) {
      function<void(Rewriter &)> f = [init, cond, sm](Rewriter &r) {
        SourceLocation loc;

        int i = randomNumber(0, 2);

        if (init != nullptr)
          loc = init->getEndLoc();
        if (i == 0 && cond != nullptr)
          loc = cond->getEndLoc();

        // 使用Rewriter替换分号为逗号
        r.ReplaceText(loc, 1, ",");

        outs() << sm->getPresumedLineNumber(loc) << " "
               << sm->getPresumedColumnNumber(loc) << "\n"
               << desc << "\n";
      };

      all.push_back(f);
    }
  }
};

} // namespace

定义了描述、id还有一个类

来看一下这个ForSemiCallback

包含一个vector引用类型的成员变量,vector<function<void(Rewriter &)>> &all;:这是一个引用类型的成员变量,用于存储函数对象的向量。并且在构造函数中初始化:

explicit ForSemiCallback(vector<function<void(Rewriter &)>> &all)
    : all(all) {}

run方法

定位两个分号的位置,随机将一个分号改成逗号

构造函数

所有错误类型的构造函数都差不多,就分析这个了

ForSemiSyntax::ForSemiSyntax(vector<function<void(Rewriter &)>> &all) {
  this->callbacks.push_back(
      unique_ptr<MatchFinder::MatchCallback>(new ForSemiCallback(all)));//把自定义的callback函数放到容器当中

  // 匹配所有for语句并且将他们的id设置为我们自定义的id,方便后续找到
  StatementMatcher m = forStmt(isExpansionInMainFile()).bind(id);

  finder.addMatcher(m, this->callbacks[0].get());//对于所有for语句,都绑定ForSemiCallback,使得所有的for都能异化
}

还有两个大家都一样的方法:

void ParenSyntax::HandleTranslationUnit(clang::ASTContext &context) {
  finder.matchAST(context);
}

string ParenSyntax::name() const { return "ParenSyntax"; }

以后就不分析了

ParenSyntax

只看run方法了

void run(const MatchFinder::MatchResult &result) override {
  const Stmt *bo = result.Nodes.getNodeAs<Stmt>(id);
  const SourceManager *sm = result.SourceManager;

  if (bo && sm) {//有效才执行
    function<void(Rewriter &)> f = [bo, sm](Rewriter &r) {
      // 左括号位置 右括号位置
      SourceLocation lParenLoc, rParenLoc;

      if (const CallExpr *callExpr = dyn_cast<CallExpr>(bo)) {//如果bo是一个函数调用表达式的话
        rParenLoc = callExpr->getRParenLoc();//那么就获取右括号的位置
        // 获取左括号的位置
        if (callExpr->getNumArgs() == 0) {
          // 如果没有参数
          lParenLoc = rParenLoc.getLocWithOffset(-1);//左括号偏移量为-1
        } else {
          // 有参数,使用第一个参数位置
          lParenLoc = callExpr->getArg(0)->getBeginLoc().getLocWithOffset(-1);
          // 向左搜寻,以确保找到左括号的位置(考虑有空格和其他字符的情况)
          while (sm->getCharacterData(lParenLoc)[-1] != '(') {
            lParenLoc = lParenLoc.getLocWithOffset(-1);
          }
        }
      } else if (const ParenExpr *parenExpr = dyn_cast<ParenExpr>(bo)) {//如果bo是一个括号表达式的话
        lParenLoc = parenExpr->getLParen();//直接获取
        rParenLoc = parenExpr->getRParen();
      } else {
        return;
      }

      char c = '(';
      SourceLocation loc = lParenLoc;
//随机删除左右括号
      int i = randomNumber(0, 2);
      if (i == 0) {
        c = ')';
        loc = rParenLoc;
      }

      r.RemoveText(loc, 1);

      outs() << sm->getPresumedLineNumber(loc) << " "
             << sm->getPresumedColumnNumber(loc) << " " << c << "\n"
             << desc << "\n";
    };

    all.push_back(f);//被掉用户后就销毁,需要手动重新push
  }
}

UndefVarSyntax

未定义变量错误,使用了未定义的变量, 现实情况可能是打错了变量名. 随机选择一个变量使用, 将它改成一个没有出现过的变量名.

void run(const MatchFinder::MatchResult &result) override {
  const DeclRefExpr *bo = result.Nodes.getNodeAs<DeclRefExpr>(id);//获得为id的变量的节点
  const SourceManager *sm = result.SourceManager;//获得资源管理器

  if (bo && sm) {
    function<void(Rewriter &)> f = [bo, sm](Rewriter &r) {
      const NamedDecl *nd = dyn_cast<NamedDecl>(bo->getDecl());//获得声明并转为名称的指针

      const string oldn = nd->getName().str();//old名称
      const string newn = oldn + randomStr(oldn.size());//新变量名

      r.ReplaceText(bo->getSourceRange(), newn);//替换变量名
      SourceLocation loc = bo->getBeginLoc();

      outs() << sm->getPresumedLineNumber(loc) << " "
             << sm->getPresumedColumnNumber(loc) << " " << oldn << " " << newn
             << "\n"
             << desc << "\n";
    };

    all.push_back(f);
  }
}

semantic包

语义错误

OperatorSemantic

操作符错误,随机把正确的操作符替换为错误的操作符

首先定义了所有的二院操作符,以二维数组的形式

vector<vector<string>> binaryOperators = {
    {"+", "-", "*", "/", "%"},          // Arithmetic
    {"<", "<=", ">", ">=", "==", "!="}, // Comparison
    {"&&", "||"},                       // Logical
    {"&", "|", "^"},                    // Bitwise
    {"=", "+=", "-=", "*=", "/=", "%="} // Assignment
};

nextStr函数

从二维数组中选取一个不同于cur的二元操作符

string nxtStr(string &cur) {
  string nxt = cur;
  for (auto &line : binaryOperators) {
    if (find(line.begin(), line.end(), cur) == line.end())
      continue;
    int s = line.size();
    // if (s == 1) break;

    int rand = randomNumber(0, s);
    nxt = line[rand];
    while (nxt == cur) {
      rand = randomNumber(0, s);
      nxt = line[rand];
    }
    break;
  }
  return nxt;
}

run函数

void run(const MatchFinder::MatchResult &result) override {
  const auto *bo = result.Nodes.getNodeAs<BinaryOperator>(id);//获得二元操作符的bo
  SourceManager *sm = result.SourceManager;//获得资源管理器
  bool isInMain = sm->isInMainFile(bo->getOperatorLoc());//判断二院操作符的位置是不是在主文件当中

  if (bo && isInMain) {
    string cur = bo->getOpcodeStr().str();//获得当前二元操作符
    string nxt = nxtStr(cur);//新操作符

    function<void(Rewriter &)> f = [bo, sm, cur, nxt](Rewriter &r) {
      const SourceLocation &loc = bo->getOperatorLoc();
      // 设置 rewriter
      r.ReplaceText(loc, cur.size(), nxt);//替换

      outs() << sm->getPresumedLineNumber(loc) << " "
             << sm->getPresumedColumnNumber(loc) << " " << cur << " " << nxt
             << "\n"
             << desc << "\n";
    };

    all.push_back(f);
  }
}

BoundSemantic

边界错误: 对数值进行了不正确的增加或减少,从而导致变量值与预期不符. 挑选随机类型为数值类型的变量 a, 在它后边插入 +/- number 的操作.

这个就比语法错误复杂很多,而且包含了两个callback类

modifyBound函数

对数值进行增加或者减少

inline void modifyBound(const DeclRefExpr *bo, const SourceManager *sm,
                        Rewriter &r) {
  // 注意 getEndLoc 可能返回代表范围结束的令牌的开始位置
  SourceLocation loc = bo->getEndLoc();
  // 返回紧随给定令牌之后的第一个字符的位置
  loc = Lexer::getLocForEndOfToken(loc, 0, *sm, r.getLangOpts());

  // 修改边界, 范围过大反而不真实
  int num = randomNumber(1, 6);
  int op = randomNumber(0, 2);

  string num_str = to_string(num);
  string op_str = ((op == 0) ? " +" : " -");

  r.InsertTextAfter(loc, op_str + " " + num_str);

  outs() << sm->getPresumedLineNumber(loc) << " "
         << sm->getPresumedColumnNumber(loc) << "\n"
         << desc << "\n";
}

举例:比如

er
int x = 10;
int y = x + 5;
	//执行
  SourceLocation loc = bo->getEndLoc();
  loc = Lexer::getLocForEndOfToken(loc, 0, *sm, r.getLangOpts());
//结果就是:
//5所在的位置

BoundCallbackComm 的run方法

第一个callback类

void run(const MatchFinder::MatchResult &result) override {
  const auto *bo = result.Nodes.getNodeAs<DeclRefExpr>(id0);//获取id为id0且为声明引用类型的节点,id0为“numvarialbe"
  const SourceManager *sm = result.SourceManager;

  if (bo && sm) {
    function<void(Rewriter &)> f = [bo, sm](Rewriter &r) {
      modifyBound(bo, sm, r);//然后全部替换为其他值
    };

    all.push_back(f);
  }
}

辅助函数isLastEquals

判断这个等号是否是最后一个赋值

bool isLastEquals(const BinaryOperator *bin, const SourceManager *sm) {
  if (!bin || !sm) {
    return false;
  }

  // 等号的确切位置
  SourceLocation loc = bin->getOperatorLoc();
  // 将 loc 移动到等号的下一个 token 位置
  loc = Lexer::getLocForEndOfToken(loc, 0, *sm, LangOptions());

  Lexer lexer(sm->getSpellingLoc(loc), LangOptions(),
              sm->getBufferData(sm->getFileID(loc)).data(),
              sm->getCharacterData(loc),
              sm->getBufferData(sm->getFileID(loc)).end());//创建lexer对象,用于对从这个loc到文件末尾的分析

  Token token;

  while (!lexer.LexFromRawLexer(token)) {//读取token,并且存在token当中
    // 如果 token 是分号 return true
    if (token.is(tok::semi)) {
      return true;
    }

    // 找到另一个等号,不是最后一个赋值
    if (token.is(tok::equal)) {
      return false;
    }
  }

  // 没有找到其他的等号,是最后一个赋值
  return true;
}

辅助函数rightDeclRefs

提取所有右值引用,放到result当中,result就是个vector的引用

void rightDeclRefs(vector<const DeclRefExpr *> &result, const Expr *e,
                   const SourceManager *sm) {
  if (!e)
    return;
#ifdef AMMON_DEBUG
  printExpr(e, sm);
#endif
  // 是否是变量使用
  const DeclRefExpr *dre = dyn_cast<DeclRefExpr>(e);//能转换成功就代表是变量引用

  // dre 不是 DeclRefExpr
  if (dre == nullptr) {
    // 在子节点中递归寻找
    for (const Stmt *child : e->children()) {
      const Expr *cur = dyn_cast<Expr>(child);
      if (cur == nullptr)
        continue;
      rightDeclRefs(result, cur, sm);
    }
    return;
  }

  // dre 是 DeclRefExpr,那么尝试转换为变量声明
  const VarDecl *vd = dyn_cast<VarDecl>(dre->getDecl());

  // dre 引用的可能不是变量声明 (而是函数声明等), 所以这里是确认 vd 是
  // VarDecl?
  if (vd == nullptr)
    return;
  // vd 是 arithmetic?
  if (!vd->getType()->isArithmeticType())
    return;

  result.push_back(dre);
}

BoundCallbackAssi 的run方法

在赋值语句中捕获右侧操作数中所有的变量引用,并将其边界进行修改

比第一个callback函数更加严谨

void run(const MatchFinder::MatchResult &result) override {
  // 获取赋值表达式 =
  const BinaryOperator *assi = result.Nodes.getNodeAs<BinaryOperator>(id1);//id1为assignment,就是等号
  const SourceManager *sm = result.SourceManager;

  if (assi && sm) {
    // 保证是直到下一个分号为止最后一个 =
    if (!isLastEquals(assi, sm))
      return;

    // 获取赋值右侧的表达式并忽略隐式的类型转换
    const Expr *rhs = assi->getRHS()->IgnoreImpCasts();

    // 收集所有的符合要求的 DeclRefExpr
    vector<const DeclRefExpr *> valid;
    rightDeclRefs(valid, rhs, sm);

    for (const DeclRefExpr *bo : valid) {
      if (bo == nullptr)
        continue;

      function<void(Rewriter &)> f = [bo, sm](Rewriter &r) {
        modifyBound(bo, sm, r);
      };

      all.push_back(f);
    }
  }
}

构造函数

有点不一样,这里考虑了两种情况

BoundSemantic::BoundSemantic(vector<function<void(Rewriter &)>> &all) {
  this->callbacks.push_back(
      unique_ptr<MatchFinder::MatchCallback>(new BoundCallbackComm(all)));
  this->callbacks.push_back(
      unique_ptr<MatchFinder::MatchCallback>(new BoundCallbackAssi(all)));

  // 寻找所有的数值类型变量使用, 并且如果上下文中有某些操作符时跳过
  // 如果有等号就忽略
  StatementMatcher m0 =
      declRefExpr(isExpansionInMainFile(),
                  to(anyOf(varDecl(hasType(realFloatingPointType())),
                           varDecl(hasType(isInteger())))),
                  unless(hasAncestor(unaryOperator(
                      hasAnyOperatorName("++", "--", "->", ".")))),
                  unless(hasAncestor(binaryOperator(hasOperatorName("=")))))
          .bind(id0);

  // 集中处理等号情况
  StatementMatcher m1 =
      binaryOperator(isExpansionInMainFile(), hasOperatorName("="),
                     unless(hasAncestor(unaryOperator(
                         hasAnyOperatorName("++", "--", "->", ".")))),
                     forEachDescendant(declRefExpr(
                         to(anyOf(varDecl(hasType(realFloatingPointType())),
                                  varDecl(hasType(isInteger())))))))
          .bind(id1);

  finder.addMatcher(m0, this->callbacks[0].get());
  finder.addMatcher(m1, this->callbacks[1].get());
}

VarUseSemantic

变量使用错误: 某个该用变量 a 的地方用成了同一类型的变量 b. 随机挑选类型 T 变量 a, 并替换为已声明的类型 T 变量 b.

Replacetool类

定义了两个id:

const string desc =
    "This bug is a semantic error where the variable <b> is mistakenly used in "
    "place of the intended variable <a> of the same type";
const string id = "numberOperand";
const string id0 = "numberOperandGet";

内部还包含了一个将bo和sm单独封装成了一个类

class ReplaceVar {
public:
  const DeclRefExpr *bo;
  const SourceManager *sm;
  ReplaceVar(const DeclRefExpr *bo, const SourceManager *sm) : bo(bo), sm(sm) {}
};

getRandomReplaceName方法

  // 随机替换变量, 返回新变量名,引用更改需要替换的bo与sm
  string getRandomReplaceName(const DeclRefExpr *decl, const DeclRefExpr *&changedDecl, const SourceManager *&changedSm) {
    // 随机排序
    shuffleVector(replaceList);

    // 循环遍历找到一个满足条件的变量
    for (auto rVar : replaceList) {
      const NamedDecl *rNamedDecl = dyn_cast<NamedDecl>(rVar.bo->getDecl());
      const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl->getDecl());
      // 不同名且同类型
      if (decl==nullptr || rNamedDecl->getNameAsString()==namedDecl->getNameAsString()) {
        continue;
      }
      string rType = getDeclType(rVar.bo);
      string declType = getDeclType(decl);
      if (rType.empty() || declType.empty() || rType!=declType) {
        continue;
      }
#ifdef AMMON_DEBUG
      INFO("**Print Type**");
      string typeInfo = namedDecl->getNameAsString()+" "+declType+" "+rNamedDecl->getNameAsString()+" "+rType;
      INFO(typeInfo);
#endif
      // 两个变量进行比较,用作用域更大的变量替换另一个
      if (isLargerScope(rNamedDecl, namedDecl)) {
        // 用vector中的变量替换传入的变量
        return rNamedDecl->getNameAsString();
      } else if (isLargerScope(namedDecl, rNamedDecl)) {
        // 用传入的变量替换vector中的变量
        changedDecl = rVar.bo;
        changedSm = rVar.sm;
        return namedDecl->getNameAsString();
      }
    }
    return "";
  }

辅助函数:isLargerScope

// srcDecl的作用域是否更大
static bool isLargerScope(const NamedDecl *srcDecl, const NamedDecl *cmpDecl) {
  const DeclContext *srcContext = srcDecl->getDeclContext();
  const DeclContext *cmpContext = cmpDecl->getDeclContext();
  while (cmpContext) {
    if (cmpContext==srcContext) {
      return true;
    }
    cmpContext = cmpContext->getParent();
  }
  return false;
}

辅助函数:getDeclType


// 获取变量类型
static string getDeclType(const DeclRefExpr *decl) {
  const ValueDecl *valuedecl = decl->getDecl();
  QualType qualType = valuedecl->getType();//c++中,变量和函数都是值声明的一种
  // 跳过typedef
  clang::Type const *underType = qualType.getTypePtr()->getUnqualifiedDesugaredType();
  // 输出string
  LangOptions lo;//语言选项
  PrintingPolicy pp(lo);//打印策略
  string s;
  llvm::raw_string_ostream rso(s);//定义一个字符串和stream对象,用于将类型写入这个字符串
  QualType::print(underType, Qualifiers(), rso, lo, llvm::Twine());//将类型信息打入rso中

  return rso.str();//返回rso中的字符串
}

辅助函数outputvar

string outputVar() {
  string declInfo;
  for (auto &replaceVar : replaceList) {
    const NamedDecl *namedDecl = dyn_cast<NamedDecl>(replaceVar.bo->getDecl());//转换为命名声明 
    declInfo += " " + namedDecl->getNameAsString();
  }
  return declInfo;
}

VarGetCallback

run方法

    void run(const MatchFinder::MatchResult &result) override {
      const DeclRefExpr *bo = result.Nodes.getNodeAs<DeclRefExpr>(id);
      const SourceManager *sm = result.SourceManager;

      if (bo && sm) {
        replaceTool.putVar(bo, sm);

#ifdef AMMON_DEBUG
        printInfo(result, "Get");
#endif
      }
    }

这个回调函数只是将匹配到的变量放到replaceList当中

VarUseCallback

真正进行替换

run方法

  void run(const MatchFinder::MatchResult &result) override {
    const DeclRefExpr *bo = result.Nodes.getNodeAs<DeclRefExpr>(id);
    const SourceManager *sm = result.SourceManager;

    if (bo && sm) {
#ifdef AMMON_DEBUG
      printInfo(result, "replace");
#endif
      const DeclRefExpr *changedDecl = bo;//替换的bo
      const SourceManager *changedSm = sm;//替换的sm

      const string newName = replaceTool.getRandomReplaceName(bo, changedDecl, changedSm);//调用获取新变量名
      if (newName.empty()) {
        return;
      }
      function<void(Rewriter &)> f = [changedDecl, changedSm, newName](Rewriter &r) {

        const NamedDecl *changedNd = dyn_cast<NamedDecl>(changedDecl->getDecl());
        const string oldName = changedNd->getNameAsString();
        r.ReplaceText(changedDecl->getSourceRange(), newName);//替换掉
        SourceLocation loc = changedDecl->getBeginLoc();

        outs() << changedSm->getPresumedLineNumber(loc) << " "
               << changedSm->getPresumedColumnNumber(loc) << " " << oldName << " " << newName
               << "\n"
               << desc << "\n";
      };

      all.push_back(f);
    }
  }

构造函数

也是add两个

VarUseSemantic::VarUseSemantic(vector<function<void(Rewriter &)>> &all) {
  this->callbacks.push_back(
          unique_ptr<MatchFinder::MatchCallback>(new VarGetCallback(all)));
  this->callbacks.push_back(
      unique_ptr<MatchFinder::MatchCallback>(new VarUseCallback(all)));

  // 所有有声明的变量使用
  StatementMatcher m =
      declRefExpr(isExpansionInMainFile(), hasDeclaration(varDecl(hasType(isInteger()))))
          .bind(id);

  finder.addMatcher(m, this->callbacks[0].get());
  finder.addMatcher(m, this->callbacks[1].get());
}

main.cpp

SilentDiagConsumer类

用于屏蔽源程序中的诊断信息,即警告和错误信息,不让其输出到控制台或其他目标。

重写了HandleDiagnostic方法,为空,也就是不做任何操作

// 用于将源程序本身的警告屏蔽掉
class SilentDiagConsumer : public clang::DiagnosticConsumer {
public:
  void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel,
                        const clang::Diagnostic &Info) override {}
};

AmmonFrontendAction类

继承了ASTFrontendAction 类,用于对Clang前端进行操作

成员变量

private:
  Rewriter rewriter;//rewriter
  // 这是不知道 rewriter 里边有 editBuffer 迭代器之前的设计...
  vector<function<void(Rewriter &)>> all;//存放函数的vector
  int typeOption;

EndSourceFileAction方法

重写的方法,在处理完源文件之后被调用

// 处理完文件后, 进行的操作
void EndSourceFileAction() override {
  auto mainFileId = rewriter.getSourceMgr().getMainFileID();//获取主文件的标识符,这个标识符用于识别源文件的位置
  int num = all.size();//有多少个修改操作,也就是rewriter

  if (num == 0) {
    // 无效的 ASTConsumer
    throw InvalidConsumerExp();
  }

  // 随机选择一个位置进行改写
  int rand = randomNumber(0, num);
  // 在这里将修改存储在 rewriter 缓冲区中
  all[rand](rewriter);

  // 将整个修改后的文件写入 llvm 输出
  rewriter.getEditBuffer(mainFileId).write(outs());
}

CreateASTConsumer方法

也是重写的方法,就是创建ASTComsumer对象用的

  unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci,
                                            StringRef file) override {
    // 屏蔽原来的错误和异常信息,用自己的SilentDiagConsumer
    ci.getDiagnostics().setClient(new SilentDiagConsumer(), true);

    rewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts());

    // 为每个文件创建一个 ASTConsumer,调用的是自己写的工厂类
    return ASTConsumerFactory::getASTConsumer(all, typeOption);
  }
};

AmmonFrontendActionFactory类

用于创建AmmonFrontedAction对象,貌似就是把他变成unique的?

class AmmonFrontendActionFactory : public FrontendActionFactory {
private:
  int typeOption;

public:
  AmmonFrontendActionFactory(int t) : typeOption(t) {}

  unique_ptr<FrontendAction> create() override {
    return make_unique<AmmonFrontendAction>(typeOption);
  }
};

一个新的namespace

namespace {

// 命令行选项
cl::OptionCategory ammonCategory("Ammon options");

// -t <type>
// magic number?
int typeOptionInit = ALL_CONSUMER;
cl::opt<int> typeOption("t", cl::desc("Specify mutation type"),//t是描述信息,表示变异类型
                        cl::value_desc("type"), cl::Optional,//
                        cl::init(typeOptionInit), cl::cat(ammonCategory));

void runTool(ClangTool &tool) {
  // 预留处理其它参数的空间
  if (ASTConsumerFactory::isValidType(typeOption)) {//选项是否合法
    try {
      tool.run(newAmmon(typeOption).get());//运行
    } catch (const InvalidConsumerExp &e) {
    } catch (...) {
      ERROR("Something Unknown Happened");
    }

    return;
  }

  // 默认是循环尝试所有 ASTConsumer, 直到有一个可以用
  while (true) {
    // 如果这次选择的 consumer 无效, 则多次选择
    try {
      tool.run(newAmmon(typeOption).get());
    } catch (const InvalidConsumerExp &e) {
      continue; // 继续循环就行了
    } catch (const exception &e) {
      ERROR(e.what());
    } catch (...) {
      ERROR("Something Unknown Happened");
    }

    break;
  }
}

} // namespace

main函数

int main(int argc, const char **argv) {
  cl::HideUnrelatedOptions(ammonCategory);//隐藏与 ammonCategory 不相关的命令行选项
  Expected<CommonOptionsParser> expectedParser =
      CommonOptionsParser::create(argc, argv, ammonCategory);//使用 CommonOptionsParser 类创建一个命令行选项解析器对象
  if (!expectedParser) {
    return 1;//创建失败直接返回
  }

  CommonOptionsParser &op = expectedParser.get();//获取解析器的引用
  // 只考虑处理一个文件
  ClangTool tool(op.getCompilations(), op.getSourcePathList());

  outs() << "==== Start " << op.getSourcePathList()[0] << " ====\n";

  runTool(tool);

  outs() << "==== End ====\n";

  return 0;
}

  目录