Почему эта команда ASM загружает 10? - PullRequest
0 голосов
/ 10 июля 2020

Я тестировал фреймворк ASM, и я почти не могу понять, почему этот фрагмент кода дает значение int 10. Для получения дополнительной информации: метод возвращает int, и я распечатал int обратно.

// This is the complete ASM visit of this method
// ...
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null);
mv.visitCode();
mv.visitIntInsn(Opcodes.BIPUSH, 0);

mv.visitIntInsn(Opcodes.BIPUSH , 0);
mv.visitIntInsn(Opcodes.ISTORE, 2);

mv.visitIntInsn(Opcodes.ILOAD, 1);
mv.visitInsn(Opcodes.IRETURN);

mv.visitMaxs(0, 0);
mv.visitEnd();
// ...

Декомпилятор показывает даже какой-то странный код. Вывод декомпилятора :

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

public class HelloWorld implements Add {
    public HelloWorld() {
    }

    public int add(int var1, int var2) {
        boolean var3 = false;
        return var1;
    }
}
  1. Почему функция возвращает 10? Это ошибка? Вызывает ли это своего рода «неопределенное поведение», потому что переменные загружаются в «self», потому что отталкиваются?

  2. Как JVM обрабатывает переменные и как их выделять, это стек исходя из ? Если так, то aload будет просто pu sh дублировать значение данных

stack before aload: var1 var2 var3
stack after aload_1: var1 var2 var3 var1

Или я совершенно не прав? Похоже, что вместо использования реестров немного переработали.

1 Ответ

2 голосов
/ 10 июля 2020

Кто написал этот байт-код? Что он должен делать? На самом деле он делает следующее:

  • pu sh (byte) 0 в стек операндов как целое число
  • pu sh (byte) 0 в стек операндов еще раз
  • извлечь самый верхний 0 из стека и сохранить как целое число в таблице локальных переменных, слот # 2
  • загрузить целое число из слота №1 таблицы локальных переменных (примечание: другой слот, чем раньше) в стек
  • возвращать самый верхний элемент стека как результат метода

Вам также необходимо знать, что в таблице локальных переменных

  • # 0 равно this для вызываемого метода, то есть ссылки на экземпляр HelloWorld,
  • # 1 - это первый параметр метода var1,
  • # 2 - второй параметр метода var2.

Итак, если метод возвращает 10, это означает, что вы, должно быть, вызвали метод со значением var1 10, потому что вы возвращаете содержимое слота №1 без изменений. Нет ни малейшего понятия, что вы добавляете сюда что-либо, как следует из названия метода. Вместо этого он делает pu sh 2x 0 в стек, но использует только один из них, перезаписывая var2 по любой причине перед возвратом var1, как я уже сказал. Затем декомпилятор пытается разобраться в этом, делая довольно хорошую работу.

Обновление: Если вы хотите реализовать это

public int add(int var1, int var2) {
  return var1 + var2;
}

, тогда ваш байт-код должен быть таким:

ILOAD 1
ILOAD 2
IADD
IRETURN
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...