如果 java 字节码中的条件反转
Reversed if condition in java bytecode
考虑简单的例子
private static String isPositive(int val) {
if (val > 0) {
return "yes";
} else {
return "no";
}
}
这里很简单:if val > 0
return yes
else return no
。
但是在编译之后,在字节码中,这个 if 条件被颠倒了:
private static isPositive(I)Ljava/lang/String;
L0
LINENUMBER 12 L0
ILOAD 0
IFLE L1
L2
LINENUMBER 13 L2
LDC "yes"
ARETURN
L1
LINENUMBER 15 L1
FRAME SAME
LDC "no"
ARETURN
它检查:if val <= 0
then return no
, else return yes
.
首先,我认为 <=
检查更便宜,而且是某种优化。但是,如果我将初始代码更改为
if (val <= 0) {
return "no";
} else {
return "yes";
}
它仍然会在字节码中反转:
L0
LINENUMBER 12 L0
ILOAD 0
IFGT L1
L2
LINENUMBER 13 L2
LDC "no"
ARETURN
L1
LINENUMBER 15 L1
FRAME SAME
LDC "yes"
ARETURN
那么,这种行为有原因吗?可以改成直截了当吗?
可能是这样做的,以便 if
中的两个代码块在翻译后的字节码中以相同的顺序显示。
例如,这个Java代码:
if (val > 0) {
return "yes";
} else {
return "no";
}
翻译成这样(伪代码):
If val <= 0, then branch to L1
return "yes"
L1:
return "no"
请注意,在原始 Java 代码中,会检查 if
条件以查看第一个代码块是否应该 运行,而在翻译后的字节码中会完成检查查看是否应该采用该分支(跳过第一段代码)。所以它需要检查一个互补条件。
Can it be changed to straightforward?
当然也可以保留条件,但是你需要将两个代码块的顺序颠倒:
If val > 0, then branch to L1
return "no"
L1:
return "yes"
虽然我不会说这个版本比上一个版本"more straighforward"。
不管怎样,你为什么要改变它?两个版本应该都可以。
原因可能是字节码用来执行其语句的操作数堆栈。
First the comparison is performed and 1, 0 or -1 is pushed onto the
operand stack. Next a branch is performed based on whether the value
on the operand stack is greater, less-than or equal to zero.
在 JavaCodeToByteCode#conditionals 中给出了很好的图形细节
还有详细的Instruction For Branching。
实际上,反转条件是最直接的编译策略。您编写 Java 匹配模式
的代码
if(condition_fullfilled) {
somecode
}
这将被编译为与模式匹配的字节码
goto_if_condition_not_fullfilled A_LABEL
compiled_somecode
A_LABEL:
因为条件分支说什么时候跳过条件代码,它的条件必须与你的源代码说什么时候执行 条件代码。
上面的示例没有 else
部分,说明了为什么没有简单的方法可以使用与源代码具有相同条件的条件分支指令来编译 if
。可以,但需要不止一条分支指令。
没有 else
的 if
语句的这种直接编译策略也可以轻松扩展以处理 else
。没有理由改变策略,例如当存在 else
子句时,切换语句的顺序。
请注意,在您的情况下,两个分支都以 return
语句结尾,
之间没有区别
if (val > 0) {
return "yes";
} else {
return "no";
}
和
if (val > 0) {
return "yes";
}
return "no";
无论如何。
考虑简单的例子
private static String isPositive(int val) {
if (val > 0) {
return "yes";
} else {
return "no";
}
}
这里很简单:if val > 0
return yes
else return no
。
但是在编译之后,在字节码中,这个 if 条件被颠倒了:
private static isPositive(I)Ljava/lang/String;
L0
LINENUMBER 12 L0
ILOAD 0
IFLE L1
L2
LINENUMBER 13 L2
LDC "yes"
ARETURN
L1
LINENUMBER 15 L1
FRAME SAME
LDC "no"
ARETURN
它检查:if val <= 0
then return no
, else return yes
.
首先,我认为 <=
检查更便宜,而且是某种优化。但是,如果我将初始代码更改为
if (val <= 0) {
return "no";
} else {
return "yes";
}
它仍然会在字节码中反转:
L0
LINENUMBER 12 L0
ILOAD 0
IFGT L1
L2
LINENUMBER 13 L2
LDC "no"
ARETURN
L1
LINENUMBER 15 L1
FRAME SAME
LDC "yes"
ARETURN
那么,这种行为有原因吗?可以改成直截了当吗?
可能是这样做的,以便 if
中的两个代码块在翻译后的字节码中以相同的顺序显示。
例如,这个Java代码:
if (val > 0) {
return "yes";
} else {
return "no";
}
翻译成这样(伪代码):
If val <= 0, then branch to L1
return "yes"
L1:
return "no"
请注意,在原始 Java 代码中,会检查 if
条件以查看第一个代码块是否应该 运行,而在翻译后的字节码中会完成检查查看是否应该采用该分支(跳过第一段代码)。所以它需要检查一个互补条件。
Can it be changed to straightforward?
当然也可以保留条件,但是你需要将两个代码块的顺序颠倒:
If val > 0, then branch to L1
return "no"
L1:
return "yes"
虽然我不会说这个版本比上一个版本"more straighforward"。
不管怎样,你为什么要改变它?两个版本应该都可以。
原因可能是字节码用来执行其语句的操作数堆栈。
First the comparison is performed and 1, 0 or -1 is pushed onto the operand stack. Next a branch is performed based on whether the value on the operand stack is greater, less-than or equal to zero.
在 JavaCodeToByteCode#conditionals 中给出了很好的图形细节 还有详细的Instruction For Branching。
实际上,反转条件是最直接的编译策略。您编写 Java 匹配模式
的代码if(condition_fullfilled) {
somecode
}
这将被编译为与模式匹配的字节码
goto_if_condition_not_fullfilled A_LABEL
compiled_somecode
A_LABEL:
因为条件分支说什么时候跳过条件代码,它的条件必须与你的源代码说什么时候执行 条件代码。
上面的示例没有 else
部分,说明了为什么没有简单的方法可以使用与源代码具有相同条件的条件分支指令来编译 if
。可以,但需要不止一条分支指令。
没有 else
的 if
语句的这种直接编译策略也可以轻松扩展以处理 else
。没有理由改变策略,例如当存在 else
子句时,切换语句的顺序。
请注意,在您的情况下,两个分支都以 return
语句结尾,
if (val > 0) {
return "yes";
} else {
return "no";
}
和
if (val > 0) {
return "yes";
}
return "no";
无论如何。