- Байт-код JVM — это компактный набор инструкций, который выполняет виртуальная машина Java.
- Компилятор Java генерирует байт-код в файлах
.class, а не в машинном коде. - Один и тот же файл
.classможет работать на любой платформе с установленной Java runtime, и это суть формата. - Моды переписывают байт-код, чтобы изменить скомпилированную игру, не имея при этом доступа к исходному коду.
Байткод JVM — это набор инструкций, который выполняет виртуальная машина Java. При компиляции исходного кода Java компилятор не генерирует машинный код для вашего процессора. Он генерирует байткод — компактный, переносимый формат, хранящийся в файлах .class. JVM считывает эти инструкции во время выполнения и преобразует их в машинный код по мере необходимости.
Где используется Opal
Opal — это мод для Fabric, и инструменты для моддинга, на которых он основан, выполняют работу с байт-кодом за вас, поэтому вам не нужно вручную работать с опкодами. Если вы хотите добавить поведение на обычном JavaScript, обратитесь к введению в скриптинг.
Как исходный код превращается в байткод
Файл .java — это текст, написанный человеком. Файл .class — это скомпилированный вывод, который запускается JVM. Компилятор читает ваш исходный код, проверяет его и записывает один файл .class на каждый класс, содержащий инструкции байткода, а также таблицу констант, таблицы методов и другие метаданные.
Байткод не привязан к какому-либо конкретному процессору. Один и тот же файл .class может работать в Windows, macOS, Linux или на любой системе с Java runtime, поскольку JVM — это слой, который знает, как выполнять инструкции на локальной машине. Именно эта переносимость является сутью формата.
Как выглядят инструкции
Байткод — это набор инструкций, основанный на стеке. Большинство операций помещают значения в операндную стек, извлекают их оттуда и помещают результат. Нет выделения регистров, о котором нужно рассуждать; модель намеренно проста, чтобы JVM могла быстро проверять и выполнять ее.
Несколько примеров опкодов дают представление:
| Опкод | Что он делает |
|---|---|
iload | Помещает локальную переменную типа int в стек |
iadd | Извлекает два целых числа из стека, помещает их сумму |
invokevirtual | Вызывает метод экземпляра |
getfield | Читает поле экземпляра |
return | Возвращается из метода |
Вы редко читаете или пишете их вручную. Инструменты генерируют и потребляют их, и здесь в игру вступает такая библиотека, как ASM.
Почему байткод важен для модов
Байткод важен, потому что он позволяет изменять поведение без исходного кода. Мод для Minecraft почти никогда не имеет исходный код игры. Вместо этого у него есть скомпилированная игра на диске. Чтобы добавить или изменить функцию, мод считывает существующий байткод, переписывает его и передает результат обратно JVM во время загрузки классов.
Именно так работают фреймворки патчинга, внедряя хуки в методы, которые им не принадлежат. Они находят нужное место в скомпилированном методе и вставляют вызов кода мода. Работа на уровне байткода позволяет моду подключаться к игре, даже не видя ни одной строки оригинального Java-кода.
Где здесь ASM
ASM — это Java-библиотека для чтения, записи и преобразования байткода. Она разбирает файл .class на события или дерево, позволяет изменять инструкции и генерирует обратно валидный файл .class. Она небольшая, быстрая и работает близко к исходному формату, поэтому более высокоуровневые инструменты моддинга строятся на её основе, а не разбирают файлы классов самостоятельно.
Большинство моддеров никогда напрямую не вызывают ASM. Они используют более высокоуровневый слой, который скрывает опкоды и предоставляет более удобный способ сказать: «запусти мой код в начале этого метода». В глубине души этот слой редактирует байткод, часто через ASM.
Часто задаваемые вопросы
Нет. Машинный код — это собственные инструкции для конкретного ЦП. Байт-код — это собственный набор инструкций JVM, который JVM преобразует в машинный код во время выполнения. Именно этот дополнительный уровень позволяет файлам .class работать на любой платформе.
Частично. Декомпиляторы восстанавливают читаемый Java-код из байткода, и у них получается довольно хорошо для обычного кода. Результат не всегда идентичен исходному, и комментарии, а также некоторые локальные имена теряются, но этого обычно достаточно, чтобы разобраться.
Поскольку моды обычно не имеют исходного кода. Они распространяются вместе с скомпилированной игрой. Переписывание байт-кода позволяет моду изменять поведение, не затрагивая и даже не имея доступа к исходным файлам Java.
Обычно нет. Фреймворки для моддинга и слои скриптов выполняют работу с байткодом за вас. Понимание того, как это работает, помогает при отладке сложного патча или создании инструментов.