Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reversed if condition in java bytecode

Consider simple example

private static String isPositive(int val) {
    if (val > 0) {
        return "yes";
    } else {
        return "no";
    }
}

Here it's pretty straightforward: if val > 0 return yes else return no. But after compilation, in bytecode, this if condition is reversed:

  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

It checks: if val <= 0 then return no, else return yes.

First, I thought that <= check is cheaper, and it's some kind of optimization. But if I change my initial code to

if (val <= 0) {
    return "no";
} else {
    return "yes";
}

it still will be reversed in bytecode:

   L0
    LINENUMBER 12 L0
    ILOAD 0
    IFGT L1
   L2
    LINENUMBER 13 L2
    LDC "no"
    ARETURN
   L1
    LINENUMBER 15 L1
   FRAME SAME
    LDC "yes"
    ARETURN

So, is there a reason for such behavior? Can it be changed to straightforward?

like image 860
esin88 Avatar asked Mar 02 '17 18:03

esin88


2 Answers

Actually, reversing the condition is the most straight-forward compilation strategy possible. You write Java code matching the pattern

if(condition_fullfilled) {
    somecode
}

which will get compiled to bytecode matching the pattern

  goto_if_condition_not_fullfilled A_LABEL
  compiled_somecode
A_LABEL:

Since the conditional branch says when to skip the conditional code, its condition must be the opposite of your source code saying when to execute the conditional code.

The example above, not having an else part, demonstrates why there is no simple way of compiling if with a conditional branch instruction having the same condition than your source code. It’s possible, but would require more than one branch instruction.

This straight-forward compilation strategy for if statements without else can easily expanded to handle else as well. There is no reason to change the strategy, e.g. switching the order of the statements, when an else clause is present.

Note that in your case, where both branches end in a return statement, there is no difference between

if (val > 0) {
    return "yes";
} else {
    return "no";
}

and

if (val > 0) {
    return "yes";
}
return "no";

anyway.

like image 138
Holger Avatar answered Nov 08 '22 03:11

Holger


It is probably done like this so that the two blocks of code in the if show up in the same order in the translated bytecode.

For example, this Java code:

if (val > 0) {
    return "yes";
} else {
    return "no";
}

Translates to something like this (pseudocode):

If val <= 0, then branch to L1
return "yes"
L1:
return "no"  

Note that in the original Java code, the if condition is checked to see if the first block of code should run, while in the translated bytecode the check is done to see if the branch should be taken (skipping over the first block of code). So it needs to check a complementary condition.

Can it be changed to straightforward?

Of course it would also be possible to preserve the condition, but then you would need to reverse the order of the two code blocks:

If val > 0, then branch to L1
return "no"
L1:
return "yes"  

I would not say that this version is "more straighforward" than the previous one, though.

Anyway, why would you want to change it? Both versions should be just fine.

like image 20
Grodriguez Avatar answered Nov 08 '22 03:11

Grodriguez