检测 Java 字节码中的循环 - 区分后端类型

Posted

技术标签:

【中文标题】检测 Java 字节码中的循环 - 区分后端类型【英文标题】:Detection of Loops in Java Bytecode - Distinguishing back edge types 【发布时间】:2016-10-26 11:50:29 【问题描述】:

背景:

在问我的问题之前,我想声明我已经检查了以下链接:

Identify loops in java byte code

goto in Java bytecode

http://blog.jamesdbloom.com/JavaCodeToByteCode_PartOne.html

我可以使用基于支配分析算法的方法检测控制流图 (https://en.wikipedia.org/wiki/Control_flow_graph) 上的反向边缘,从而检测字节码(类文件)中的循环。

我的问题:

检测到循环后,您最终可能会有两个循环(由两个不同的后边缘定义)共享同一个循环头。正如我所意识到的,这可以通过以下两种情况创建:(案例 1)在源代码中,您有一个带有 continue 语句的 for 或 while 循环,(案例 2)在源代码中,您有两个循环 - 一个外循环是一个do-while和一个内循环;并且这些循环之间没有指令。

我的问题如下:只看字节码,如何区分这两种情况?

我的想法:

在 do-while 循环中(即没有任何 continue 语句),您不会期望 go-to 语句返回到循环头部,换句话说,不会创建后沿。

对于 while 或 for 循环(也就是没有任何 continue 语句),似乎可以有一个 go-to 语句(我不确定是否必须是一个)。我的编译器在循环之外生成(我使用的是标准 1.7 编译器)这个 go-to 指令,而不是作为给定链接中提到的后沿(这个 go-to 语句创建到头部的控制流循环,但不是从循环末尾跳回)。

所以,我的猜测是,(重复,如果有两个后沿),如果其中一个是由 go-to 语句创建的后沿,那么源代码中只有一个循环,它包括一个continue 语句(案例 1)。否则,源代码中有两个循环(案例2)。

谢谢。

【问题讨论】:

【参考方案1】:

当两个循环相等时,你所能做的就是取最简单的一个。

例如没有办法区分while (true)do while (true)for (;;)

如果您有do something(); while (false),则此循环可能根本不会出现在字节码中。

【讨论】:

【参考方案2】:

作为Peter Lawrey already pointed out,无法通过查看字节码来确定源代码形式。为了命名一个更接近您意图的示例,以下单循环代码

do action(); while(condition1() || condition2());

产生完全与嵌套循环相同的代码

do do action(); while(condition1()); while(condition2());

同样的循环

do 
    action();
    if(condition1()) continue;
    break;
 while(condition2());

产生与

完全相同的代码
do action(); while(condition1() && condition2());

与当前的javac,而令人惊讶的是

do 
    action();
    if(!condition1()) break;
 while(condition2());

没有,它只显示了确切的形式在多大程度上取决于编译器内部。 javac 的下一个版本可能会以不同的方式编译它们。

【讨论】:

以上是关于检测 Java 字节码中的循环 - 区分后端类型的主要内容,如果未能解决你的问题,请参考以下文章

反射机制

java 反编译

在 LLVM 字节码中查找循环

如何识别Java字节码中的覆盖方法?

Java中有几种类型的流

Java中的字节和字符