我使用ASM框架来操作一些java字节码。我只需要从开关指令中删除break语句。我的尝试删除了字节码中的goto指令,但不仅仅是这些与switch连接的指令(例如所有来自class…)。
你觉得怎么样?
在Java源代码中的BREAK语句和Java字节码中的任何内容之间没有显式链接。一些带有BREAK语句的语言结构可能会被编译成GOTO操作码,但我怀疑你能否在它们之间建立可靠的链接。
您唯一能做的就是捕获Java源代码中BREAK语句的行号(假设这些行没有任何其他语句),然后使用用行号信息编译的字节码可以找到这些行的操作码。
如果goto
指令出现在两个开关字节码指令之一的上下文中,则属于switch
语句。棘手的部分是确定它们是否代表break
。lookupswitch
和tableswitch
都有一个分支目标列表,如果在前面的指令是goto
指令,那么它可能代表break
。这可以通过检查所有或至少大部分goto
指令是否具有相同的目标来验证,该目标将是switch
语句之后的下一条指令。如果您已经确定了switch
语句后面指令的字节码位置,则可以将该位置的所有goto
都视为break;
。
但是这样的启发式可能会严重失败。考虑下面的代码:
outer: for( … ) {
…
inner: for(…) {
switch(…) {
case 1: …
continue inner; // jumps to the next iteration of inner
case 2: …
continue outer; // jumps to the next iteration of outer
case 3: …
// a break: formally jumps to the end of the switch but since
// there is no follow-up statement, most compilers will optimize
// this to a jump to the next inner iteration just like <continue>
break;
case 4:
…
// no break but nonetheless will be followed by a <goto>
}
}
}
一般来说,Java代码中的所有非异常、无条件分支都被编译为goto
(或goto_w
)。这包括break
语句、continue
语句、无条件循环和任意数量的控制流模式。您将无法在Java代码中的break
语句到goto
操作码之间派生出任何简单的映射。你可以通过做一些控制流分析来确定哪些跳跃像开关break
一样,但它不会是完美的。
switch
语句,并弄清楚如何将跳转表示为break
、continue
等。Procyon和Krakatau都是开源的。我编写了前者,但是代码库很大且令人生畏,因此它可能不是最佳选择。