Отменено, если условие в байт-коде Java

Рассмотрим простой пример

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

Здесь все довольно просто: если val > 0 вернуть yes еще вернуться no, Но после компиляции, в байт-коде, это если условие полностью изменено:

  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

Проверяет: если val <= 0 затем вернитесь no, иначе возврат 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

Итак, есть ли причина для такого поведения? Можно ли изменить это на прямое?

3 ответа

Решение

Вероятно, это делается так, чтобы два блока кода в if отображаются в том же порядке в переведенном байт-коде.

Например, этот код Java:

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

Переводит что-то вроде этого (псевдокод):

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

Обратите внимание, что в исходном коде Java if проверяется условие, чтобы увидеть, должен ли выполняться первый блок кода, в то время как в переведенном байт-коде выполняется проверка, чтобы определить, должна ли быть выполнена ветвь (пропуская первый блок кода). Поэтому необходимо проверить дополнительное условие.

Можно ли изменить это на прямое?

Конечно, было бы также возможно сохранить условие, но тогда вам нужно будет изменить порядок двух кодовых блоков:

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

Я бы не сказал, что эта версия "более прямолинейна", чем предыдущая.

В любом случае, почему вы хотите изменить это? Обе версии должны быть в порядке.

На самом деле, изменение условия является самой простой стратегией компиляции. Вы пишете код Java, соответствующий шаблону

if(condition_fullfilled) {
    somecode
}

который будет скомпилирован в байт-код, соответствующий шаблону

  goto_if_condition_not_fullfilled A_LABEL
  compiled_somecode
A_LABEL:

Так как условная ветвь говорит, когда пропустить условный код, его условие должно быть противоположным вашему исходному коду, когда нужно выполнить условный код.

Пример выше, не имеющий else часть, демонстрирует, почему нет простого способа компиляции if с условной инструкцией ветвления, имеющей то же условие, что и ваш исходный код. Это возможно, но потребует более одной инструкции ветвления.

Это простая стратегия компиляции для if заявления без else может легко расширяться для обработки else также. Нет причин менять стратегию, например, менять порядок операторов, когда else пункт присутствует.

Обратите внимание, что в вашем случае, когда обе ветви заканчиваются return заявление, нет никакой разницы между

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

а также

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

тем не мение.

Возможно, причина в стеке операндов, который использует байт-код для выполнения своих операторов.

Сначала выполняется сравнение и 1, 0 или -1 помещается в стек операндов. Затем выполняется ветвление в зависимости от того, больше ли значение в стеке операндов, меньше или равно нулю.

В этом есть хорошая графическая деталь, приведенная в условных обозначениях JavaCodeToByteCode#, а также подробная инструкция по ветвлению.

Другие вопросы по тегам