Что уже работает в SOOT
Это снимок состояния проекта на момент коммита fix: virtual method dispatch for basic-derived types. Здесь только то, что работает и проверено в REPL или в тестах. Планов и обещаний нет.
Цель документа: честно показать, что уже можно брать и использовать, а что ещё нет.
Язык и интерпретатор (Script)
Интерпретатор работает поверх чтения S-выражений.
- Чтение кода. Символы, числа (целые и вещественные), строки, ключевые слова,
#tи#f, кавычка, списки, а также#fooкак литерал идентификатора строки (SID). - Базовые формы.
define,lambda,if,when,unless,cond,let,set!,quote,begin. - Макросы.
defmacroработает и в интерпретаторе (lib.st), и в компиляторе (lib.sc). Это два независимых набора макросов: функцияtry_getting_macro_from_sootищет только в окружении*soot-env*. - Функции. Вызов по имени, рекурсия и замыкания (в интерпретаторе).
- Циклы.
whileс необязательной меткой:label. - Ввод и вывод.
_print,_formatи работа со строками.
Интерпретатор используется в REPL (--interpret-only, (sc-eval ...)), и на нём же построен компилятор.
Компилятор (Sootc)
Компилятор переводит формы SOOT в байткод виртуальной машины Carbon.
- Функции.
defun,lambda,defmethod. Параметры можно писать с типом,(x int), или без типа: тогда этоobject. Работают возврат значений, константы, локальные переменные и временные регистры. - Структуры.
deftypeс полями и наследованием (отbasicилиstructure). Поддерживаются опции:methods,:size-assert,:method-count-assert,:heap-base,:pack-me. - Перечисления.
defenum. - Константы.
defconstant: значение подставляется при компиляции. - Нативные функции.
define-externиdefine-nativeс сигнатурой(function ...). Вызываются инструкциейCallFf. - Статические данные.
(define name (new 'static Type ...)), в том числе с вложенными безымянными структурами. - Данные в куче.
(define name (new 'heap Type ...)). - Массивы внутри данных.
(new 'static 'inline-array T N ...)и(new 'heap 'inline-array T N ...). Доступ к элементу:(at arr i). - Вызов методов.
(-> obj method). Для типов, производных отbasic, вызов полиморфный, черезCallMethod(подробнее вdocs/polymorphism.md). Для типов, производных отstructure, вызов статический, черезLookupPointerиCall. - StateScript.
deftypeс:states. Обработчики:enter,:exit,:code,:trans,:post,:eventи треки внутри состояний. require. Подключает файл по путям поиска. Один файл не загружается дважды.seval. Вычисляет форму интерпретатором прямо во время компиляции.update-macro-metadata. Заглушка для инструментов: ничего не делает.
Виртуальная машина (Carbon)
- Процессор. Регистровая машина с 34 регистрами:
r0–r23для локальных значений,r24–r33для аргументов. Есть арифметика, сравнения, битовые операции, переходы, загрузка и сохранение, вызовы (Call,CallFf,CallMethod),LookupPointerиLoadStatic*. - Память. Арены и сборщик мусора, который обходит объекты по описаниям типов (
TypeRegistry). Указатели в структурах описываются черезPtrField, а для особых случаев можно задать свою функцию обхода (mark). Из REPL доступныgc::collect(),(gc)и(gcls). - Процессы. Планировщик (
Scheduler) сspawn,tickиrun_loop. Нативная функция может уступить управление другим процессам (should_yield). - Состояния и треки.
StateScriptхранится как данные:SsState,SsOnBlock,SsTrackGroup,SsTrack,SsLambda. Одно состояние может запускать несколько треков одновременно, и каждый трек отвечает за свой аспект анимации. - Модули. Загрузка (
load_module,load_module_from_binary,load_module_with_imports), выгрузка (unload_module) и перезагрузка (reload_module). Реестр модулей (ModuleRegistry) хранитss_type_sidsдля отката. - Глобальные символы.
Globals— плоская таблица «SID → указатель». Можно зарегистрировать символ (register_symbol), найти его (find_symbol_ptr) и удалить все символы модуля (remove_module_symbols).
Бинарный формат (.bin)
- Устройство файла. Заголовок
DC_Header, таблица записейDCEntry, данные, пул строк и таблица релокаций (битовая карта: по биту на каждые 8 байт). - Релокация. В слоте хранится смещение в файле, а в карте отмечен бит. При загрузке
read_reloc_tableприбавляет к смещению адрес файла в памяти, и получается абсолютный указатель. Механизм работает для функций, строк и указателей на статические данные и пока не менялся. - Секции.
ScriptLambda,SsType,StateScript,DataStruct,Array,Map. SsType. Массив полейSsField[]и массив методовSsMethod[]. Указатель на лямбду метода (m_pLambda) хранится как смещение в файле и релоцируется.StateScript.SsOptions,SsState[],SsOnBlock[],SsTrackGroup,SsTrack[],SsLambda[].- Загрузка.
BinaryFile::from_pathиBinaryFile::from_bufferчитают файл,read_reloc_tableприменяет релокации, аget_unmappedвозвращает данные в исходном виде для сохранения.
REPL (Sootc)
- Команды.
(help),(clear),(e),(reload),(sizes),(inspect-env),(inspect-env-soots),(has-macro "name"),(list-symbols),(save-session),(sc-run name args...),(sc-eval expr),(arg-set "key" value),(arg-get "key"),(arg-ls),(gc),(gcls),(ts-info name),(ast). - Параметры компилятора.
print-listing,print-ir,print-ast,print-asm,log-level. Только для чтения:sidbase,runtime,user-profile. - Удобство ввода. История, подсказки, автодополнение и подсветка (библиотека replxx).
- Инкрементальная работа. Каждый ввод получает своё имя
<repl-N>. Еслиdeftypeиdefmethodвводятся отдельно, они всё равно собираются в одну структуру (owners_to_rebuild). - Откат при ошибке. Состояния
FileNodeиGlobalNodeсохраняются как снимок (Snapshot). Если компиляция не удалась, изменения откатываются, и сессия остаётся рабочей.
Что пока не работает или работает частично
Этот раздел так же важен, как предыдущие: без него список выше превращается в рекламу.
- Переопределение метода.
defmethodдля уже определённого метода заменяет старыйFunctionNodeв структуре и пересобирает её. Но это не покрыто тестами и проверено не во всех случаях (другой модуль, нативный метод). - Проверка полноты методов.
deftypeс:methodsне требует реализовать все объявленные методы.save_binaryвыдаёт предупреждение, а компилятор не останавливается. Вызов(-> obj method)для нереализованного метода падает во время выполнения сUndefinedFunction. build_ss_type_for_script. УказательSsMethod::m_pLambdaзаписывается черезpush_value_with_ptr, поэтому при загрузке смещение будет учтено дважды. ПокаStateScriptне тестируется, ошибка не видна, но она отмечена.- Полиморфизм между модулями.
requireзагружает модуль, но не проверяет, совместимы ли его методы с текущимdeftype. - Макросы в
defmethod. Раскрытие макросов в теле метода работает, но тестов нет. - Сериализация
StateScript. Код есть, но полный сценарий (создать state-script, запустить, дождаться трека) не проверен.
Итог
SOOT умеет читать S-выражения, интерпретировать их и компилировать в байткод. Он загружает байткод в виртуальную машину и выполняет функции, работает со структурами и перечислениями, вызывает методы статически и полиморфно, собирает и загружает StateScript с состояниями и треками, запускает процессы и планирует их выполнение, собирает мусор, сохраняет и загружает .bin с релокациями.
SOOT пока не умеет проверять, что объявленный метод реализован, и проверять контракт при переопределении методов. Он не работает с StateScript от начала до конца и не проверяет типы между модулями.
Это молодой проект. Список выше — точка отсчёта.