Crazy
Developer
Мое видение? Легко. Поток сознания. Преднамеренно не буду перечитывать при публикации.Автор оригинала: si
своими словами изложить СВОЕ видение данного вопроса на текущий момент истории ?
Начнем с интерпретаторов и компиляторов. Сама идея "чистых концепций" себя изжила полностью.
Навскидку я не назову ни одного реально применяемого интерпретатора, который не производит предварительную обработку -- компиляцию -- исходного кода, получая и храня для будущего использования ее в некотором более близком к машине виде.
Та же фигны, но с другой стороны, происходит с компиляторами. RTTI, которое есть ни что иное, как возвращение данных языка высокого уровня обратно в машинный код, есть уже во всех реально применяемых компиляторах (ну кроме, разве что, C--).
Идеальным сочетанием здесь является juice, в котором компилятор порождает и сериализует в объектный файл дерево разбора. Это дает нам следующие плюсы:
1. Компилятору неважна целевая платформа. Вообще.
2. Исполняющей среде неважен язык. Вообще.
3. Наличия дерева разбора позволяет эффективно строить код под целевую среду.
Третий пункт -- это как раз то, во что уперлись разработчики первых JIT'ов. Дело в том, что архитектура JVM очень отличается от архитектуры интеловских процессоров. Кардинально.
Соответственно, очень трудно, имея программу в машинных кодах JVM (я преднамеренно не буду пока употреблять термин "байткод"), очень трудно породжить оптимальный код для нативной архтектуры. Результатом стала совершенно ублюдочная скорость первых JIT'ов (не говоря уж о чистой интерпретирующей JVM).
Навскидку не скажу, кому первому пришла в голову здравая мысль, но очень быстро из "а что же делать" проблема переросла в "это очевидно". Суть в том, что имея машинный код можно провести операцию, обратную компиляции. Т.е. восстановить дерево разбора. А уж на него натравить оптимизирующий кодогенератор.
Здесь разработчики дружно воспели хвалу горе-разработчикам компилятора javac, который нихрена не умеет оптимизировать. Их раздолбайство и спасло Java.
Соответственно, сейчас любая серверная JVM (думаю, нет нужды говорить, что их делает не только Sun), вооруженная знанием о том, как работает javac, шустро декомпилирует код и компилирует его заново. В родные, нативные коды.
В результате в некоторых тестах Java обставляет C++. Но странного в этом, как мы видим, в этом нет ни капли.
Разработчики исполняющей среды .net тоже наклали на juice, но все же перед их глазами было наглядно распято тельце зверька под названием "чистая интерпретирующая JVM". Это зрелище так их потрясло, что они решили добавить в машинный код своей платформы дополнительные метаданные, которые позволяли легко и почти без извращений докомпилить в нативный код. По крайней мере, для интелов это сработало.
Теперь к байткодам: начнем с того, что первым примером успешного и раскрученного переносимого компилятора был UCSD Pascal. Его компилятор формировал машинный код некоей не существовавшей в железе пи-машины. И была написана исполняющая среда p-system.
Успех породил многочисленные подражания. Поскольку использовать термин пи-код было спорно с точки зрения авторских прав, то ему придумали замену. У пи-кода была особенность: его команды, если мне не изменяет память, в массе своей были байтовыми. Соответственно "байткод" было очевидной для многих заменой.
Спустя какое-то время термин "байт-код" прилепился ко всем случаям, когда компилятор создавал код в системе команд отличной от нативной. Соответственно, сейчас говоря о байт-коде уже не имеет смысла делать предположения о том, что в нем преобладают однобайтовые команды.
Теперь вернемся к Zend. По имеющимся косвенным данным -- это байткод. Т.е. именно машинный код вирутуальной zend-машины, а не дерево разбора. Как водится, дополненный метаданными, которые, судя по косвенным данным, предназначены для упрощения отладки скрипта, но не для его оптимизации. Вероятность моей ошибки здесь высока.
Можно ли восстановить исходный код из байткода? Обычно -- да. Причина здесь в том, что, как было показано выше на примере Java, разработчики системы команд и исполняющей системы обычно не делают существенной оптимизации на уровне компилируемого машинного кода, подразумевая, что оптимизация либо не будет нужна вовсе (Tcl, JavaScript), либо что она будет сделана перекомпиляцией, а здесь предварительная оптимизация только вредит.
Можно ли реализовать eval для таких систем? Как ни странно, ответ вообще не зависит от "нативности" кода. Т.е. пытаясь реализовать eval для системы с пи-кодом или для интеловского процессора мы столкнемся с одной и той же проблемой. И это естественно ввиду отсутствия концептуальной разницы между нативным кодом и пи-кодом.
Перед нами встанут такие проблемы:
1. Обеспечение языкового окружения при компиляции (для этого есть специальное слово, но меня ломает его вспоминать). Т.е. если мы пишем eval('a=b'), то компилятор должен откуда-то узнать, что a -- это байтовая переменная, а b -- интовая. И выругаться.
2. Обеспечение связывания. После компиляции код должен быть временно (это важно) быть встроен в нужное место. К примеру, Turbo Pascal умеет генерировать код для работы с матемаьтикой, который при первом использовании замещается нативными командами для использования сопроцессора, либо для обращения к эмулятору -- но делается это раз и навсегда. До выгрузким программы. Мы же должны обеспечить выгрузку кода, который создал eval.
Для решения обеих задач нам нужны метаданные. Технических проблем с их порождением нет. Соответственно, реализуемость eval зависит только от того, насколько он был интересен разработчикам.
Поскольку у меня уже руки устли, на этом остановимся.
Или к чему-то вернемся подробнее?
P.S. Все факты изложены по памяти. Мог наврать. Но мое личное мнение это отражает.