Здравствуйте, elmal, Вы писали:
E>Здравствуйте, Sinclair, Вы писали:
S>>Задача — сделать компилятор воображаемого языка.
S>>В JVM — потому, что это проще, чем в натив (и даже в LLVM).
S>>В принципе, можно и Java-код порождать, но это не даст студентам понимания устройства байт-кода
E>А зачем какие то библиотеки? Почему нельзя тупо писать байтики напрямую? Одно дело когда ты байткод сам анализируешь, тут библиотека поможет и снимет куча геморроя. Но если задача байткод генерить, да еще в учебных целях — ИМХО лучше тупо в файлик сразу байты писать да и все. Если не ставится задача вообще все покрыть, а чтоб было покрытие ограниченного подмножества — достаточно просто быстро и интересно делается.
Чтобы генерить байтики, нужно знать, какие байтики генерить.
Я не очень разбираюсь в JVM-ном байткоде, но из опыта дотнетного байткода уже понятно, что
а) мнемоники команд. Банальная штука типа "положи в стек целую константу" требует енкодинга команды и енкодинга константы. Делать это каждый раз руками? Код становится невозможно даже прочитать, не то что поддерживать. Интуитивно хочется иметь готовую функцию типа EmitLoadConstant(x byteCodeStream, int value) или byteCodeStream.EmitLoadConstant(value).
б) goto. Не хочется вручную вычислять байтовые смещения с риском промахнуться. Удобнее иметь функции вида MarkLabel() c возможностью использовать результат этой функции в эмите jump-инструкций
в) метаданные. Банальный Call использует в аргументах method token, который весьма нетривиально устроен. Вычислять его вручную — крайне громоздко.
г) предсказание будущего. Каждый метод указывает в заголовке размер байт-кода, количество переменных, а также максимальную глубину стека. При кодогенерации это довольно-таки тяжело оценить заранее — так что либо делать два прохода по внутреннему представлению, либо, всё-таки, инкапсулировать это всё в некий класс, отвечающий за порождение байтиков, который всё это рассчитает самостоятельно с гарантиями корректности и без замусоривания пользовательского кода.