Java解析器:解析并生成Java代码

JavaParser: parsing and generating Java code

我已阅读 JavaParser manual 并开始构建我自己的示例。我打算实现的是阅读 Java 代码并在其上插入新的代码行。具体来说,我想在每个 ifwhile 语句之前初始化一个计数器,并在语句体内递增计数器。我这样做的目的是 运行 指定一组 运行 的新代码,并观察每个分支执行了多少次。我正在使用 JavaParser 来解析和添加代码,因为我想自动生成和 运行 所有内容。

例如,我们有下面一段简单的代码:

if (x>4) { 
   x=4; 
} 
else { 
   x=4; 
} 
while (i<x) {
   i++;
} 

在解析之后我想要如下内容:

int countA=0; 
int countB=0;            
if (x>4) { 
   x=4; 
   countA++;
} 
else { 
   x=4; 
   countB++;
} 
int countC=0;
while (i<x) {
   i++;
   countC++;
} 

到目前为止我的代码:

public static void main (String[] args) throws Exception {          
              CompilationUnit cu = StaticJavaParser.parse("class X{void x(){" +
              "    if (x>4) {  " +
              "      x=4;  " +
              "    }  " +
              "    else {  " +
              "        x=4;  " +
              "    }  " +
              "    while (i<x) {  " +
              "        i++;  " +
              "    }  " +
              "    }}");
                              
              cu.findAll(Statement.class).forEach(statement -> {             
                  if (statement.isIfStmt()) {
                      //System.out.println(statement.getChildNodes().get(0));
                     // System.out.println(statement.getParentNodeForChildren().toString());
                     // statement.getChildNodes().add(statement.getChildNodes().get(0));
                
                     // System.out.println(statement.toString());
                  }
                  if (statement.isWhileStmt()) {
                     // System.out.println();
                  }
              });
              System.out.println(cu);
            }
}

我尝试了一些事情(推荐的行)但没有成功。我的主要问题是我找不到在给定 statementchildNodes 末尾注入任何代码行的方法,这应该是计数器的增量或 parentNode 这将是初始化。有什么方法可以实现我所描述的吗?

您的代码中的问题如下:

if (statement.isIfStmt()) {

条件清楚地表明您正在处理 if 语句,因此您实际上不能添加指令。 你需要做的是获取第n+1个元素(其中n是if语句的索引)然后使用:

cu.findAll(Statement.class).get(2).asBlockStmt().addStatement("countA++;")

有几种方法可以定义要添加的表达式,这里为了清楚起见我使用了一个普通字符串,但请查看工作示例以了解实际语法。

虽然您可以使用相同的技术,但它会变得更加复杂。为简单起见,您的计数器声明将使用以下方法添加到方法的开头:

((BlockStmt)cu.findAll(Statement.class).get(6).getParentNode().get()).addStatement(0, new StringLiteralExpr("int b = 1;"));

解释

  • 所有语句列表的第六条语句是while循环
  • 这条语句的父节点是方法的主块,也是您要添加指令的地方
  • 此对象需要 class 转换为 BlockStmt 才能使用 addStatement 方法
  • StringLiteralExpr 是 Expression 的子class,它在这里用于从字符串创建实例。您可以探索许多其他不同的实现方式。

我复制了您的代码,使 JUnit 5 可以正常工作,这就是我得到的结果

public class ParserTest {

String counterName = "count";
Integer counterIndex = 1;

@Test
public void main() {

    CompilationUnit cu = StaticJavaParser.parse("class X{void x(){" + "    if (x>4) {  " + "      x=4;  "
            + "    }  " + "    else {  " + "        x=4;  " + "    }  " + "    while (i<x) {  " + "        i++;  "
            + "    }  " + "    }}");

    cu.findAll(Statement.class).forEach(statement -> {
        if (statement.isIfStmt()) {

            // Add counter declaration in main block for the if
            String counterNameAndIndexIf = counterName + counterIndex++;
            // Create expression using StaticJavaParser
            Expression ifCounterExpression = StaticJavaParser
                    .parseVariableDeclarationExpr(" int " + counterNameAndIndexIf + " = 0");
            ((BlockStmt) statement.getParentNode().get()).addStatement(0, ifCounterExpression);

            // Add counter declaration in main block for the else
            String counterNameAndIndexElse = counterName + counterIndex++;
            // Create expression using StaticJavaParser
            Expression elseCounterExpression = StaticJavaParser
                    .parseVariableDeclarationExpr("int " + counterNameAndIndexElse + " = 0");
            ((BlockStmt) statement.getParentNode().get()).addStatement(0, elseCounterExpression);

            // Add if increment
            Expression ifIncrementExpression = StaticJavaParser.parseExpression(counterNameAndIndexIf + "++");
            ((BlockStmt) statement.getChildNodes().get(1)).addStatement(ifIncrementExpression);

            // Add else increment
            Expression elseIncrementExpression = StaticJavaParser.parseExpression(counterNameAndIndexElse + "++");
            ((BlockStmt) statement.getChildNodes().get(2)).addStatement(elseIncrementExpression);
        }
        if (statement.isWhileStmt()) {
            String counterNameAndIndexWhile = counterName + counterIndex++;
            Expression whileCounterExpression = StaticJavaParser
                    .parseVariableDeclarationExpr(" int " + counterNameAndIndexWhile + " = 0");
            ((BlockStmt) statement.getParentNode().get()).addStatement(0, whileCounterExpression);

            // Add while increment
            Expression whileIncrementExpression = StaticJavaParser.parseExpression(counterNameAndIndexWhile + "++");
            ((BlockStmt) statement.getChildNodes().get(1)).addStatement(whileIncrementExpression);

        }
    });
    System.out.println(cu);
}

结果是

class X {

void x() {
    int count3 = 0;
    int count2 = 0;
    int count1 = 0;
    if (x > 4) {
        x = 4;
        count1++;
    } else {
        x = 4;
        count2++;
    }
    while (i < x) {
        i++;
        count3++;
    }
}

希望这对您有所帮助。 如果您需要说明如何构建这些对象,请告诉我。 肯定有更聪明的方法和代码显然可以重构,但我想尽可能清楚地为您列出。

干杯

编辑:为了支持语句嵌套并在方法的主块中声明所有计数器变量,可以使用以下方法

statement.findRootNode().getChildNodes().get(0).getChildNodes().get(1).getChildNodes().get(2)

这种方法将从根开始自上而下,并始终指向主块。 用它来添加你的计数器变量。