RFC-026b: Инструментарий yx-bindgen
Родительский RFC: RFC-026: Основной механизм FFI
Зависимость: Реализация данного RFC зависит от предварительного внедрения основного механизма FFI из RFC-026.
Аннотация
yx-bindgen механически преобразует заголовочные файлы C в файлы FFI-привязок .yx, генерируя привязки библиотек, разделение типов (непрозрачные дескрипторы / прозрачные типы), объявления функций и привязки методов.
Два основных принципа:
- Механический вывод черновика без угадывания владения — владение определяется системой типов YaoXiang, пользователь подтверждает по документации C
- Гарантия правильной раскладки для платформы — размеры полей и выравнивание прозрачных типов вычисляются для целевой платформы, обеспечивая бинарное соответствие структурам C
Мотивация
Ручное написание FFI-привязок утомительно и подвержено ошибкам — библиотеки C содержат десятки или сотни функций. Ещё опаснее раскладка прозрачных типов: при ручном написании Timespec: Type = { tv_sec: Int64, tv_nsec: Int64 } размеры полей, выравнивание и дополнение должны точно соответствовать структуре C struct timespec целевой платформы, иначе C будет читать/записывать по неправильной раскладке, что приведёт к выходу за границы (гарантия доверия из RFC-026 §2.2). yx-bindgen механически вычисляет раскладку из .h-файлов, устраняя этот риск ручной гарантии.
Предложение
1. Использование
yx-bindgen --header /usr/include/sqlite3.h --lib sqlite3 --output sqlite3_bindings.yx--lib указывает имя связываемой библиотеки, генерируется заголовок Native.c("libsqlite3").
2. Генерируемое содержимое
yx-bindgen создаёт четыре типа содержимого:
- Заголовок привязки библиотеки:
lib = Native.c("libxxx") - Разделение типов: непрозрачные указатели → непрозрачные дескрипторы; структуры данных → прозрачные типы (с раскладкой)
- Объявления функций: привязки
lib("symbol") - Привязки методов:
Type.methodили[N]
3. Автоматическое определение разделения типов
yx-bindgen определяет категорию по способу использования типа C (RFC-026 §2):
| Тип C | Определение | Вывод yx-bindgen |
|---|---|---|
typedef struct T T; (неполный тип/только указатели) | Чёрный ящик → непрозрачный дескриптор | T: Type = lib("T") |
struct T { fields }; (поля видимы, читаются/пишутся) | Данные → прозрачный тип | T: Type = { ...раскладка } |
int/long/float/double | Значение | Int32/Int64/Float32/Float64 |
char* (параметр/возврат) | Значение (копирование) | String |
void* | Без типа | *Void (системный уровень) |
Неполные типы (typedef struct sqlite3 sqlite3; без определения тела) → всегда непрозрачные дескрипторы. Полностью определённые структуры → прозрачные типы, раскладка полей вычисляется механически.
4. Вычисление раскладки (ключ к прозрачным типам)
Для полностью определённых структур yx-bindgen вычисляет смещение, размер и выравнивание каждого поля согласно ABI целевой платформы:
struct timespec {
time_t tv_sec; // платформо-зависимо: Linux x86_64 = 8 байт
long tv_nsec; // 8 байт
};// Целевая платформа Linux x86_64
Timespec: Type = {
tv_sec: Int64, // offset 0, size 8
tv_nsec: Int64 // offset 8, size 8
}
// Общий размер 16, выравнивание 8 —— бинарное соответствие структуре CПлатформенные различия: размеры time_t, long, size_t меняются в зависимости от платформы. yx-bindgen выбирает правильное отображение согласно --target, гарантируя посимвольное соответствие генерируемых прозрачных типов структурам C целевой платформы. Это ключевая ценность для устранения риска ручной гарантии раскладки из RFC-026 §2.2.
5. Пример генерации
Входные данные (абстракция sqlite3.h):
typedef struct sqlite3 sqlite3; // неполный → чёрный ящик
typedef struct sqlite3_stmt sqlite3_stmt;
int sqlite3_open(const char *filename, sqlite3 **ppDb);
int sqlite3_close(sqlite3 *db);
int sqlite3_exec(sqlite3 *db, const char *sql, ...);Выходные данные (sqlite3_bindings.yx):
// sqlite3_bindings.yx —— автоматически сгенерировано, не редактировать вручную
// ============================================================================
// Привязки библиотеки
// ============================================================================
sqlite3 = Native.c("libsqlite3")
// ============================================================================
// Типы (неполные типы → непрозрачные дескрипторы)
// ============================================================================
SqliteDb: Type = sqlite3("sqlite3")
SqliteStmt: Type = sqlite3("sqlite3_stmt")
// ============================================================================
// Функции + привязки методов
// ============================================================================
SqliteDb.open: (filename: String) -> ?SqliteDb = sqlite3("sqlite3_open")
SqliteDb.exec: (sql: String) -> Int32 = sqlite3("sqlite3_exec")
SqliteDb.close: () -> Int32 = sqlite3("sqlite3_close")
// ============================================================================
// Деструктор (опционально, подтверждается пользователем)
// ============================================================================
SqliteDb.drop = SqliteDb.close6. Корректировка пользователем
Сгенерированные привязки — черновик, пользователь подтверждает семантику владения по документации библиотеки C (yx-bindgen не угадывает):
// Сгенерировано: String по умолчанию (копирование) —— обычно корректно
SqliteDb.errmsg: () -> String = sqlite3("sqlite3_errmsg")
// getenv возвращает статическую область, не нужно копировать и не нужно захватывать —— пользователь исправляет на сырой указатель
getenv: (name: String) -> *const U8 = Native.c("libc")("getenv")
// Дескриптор принадлежит библиотеке, пользователь подтверждает привязку .drop
SqliteDb.drop = SqliteDb.close // сгенерировано как предложение, пользователь подтверждаетКлючевой момент: раскладка механически гарантируется yx-bindgen (платформо-корректность), но семантика владения (нужен ли .drop, является ли char* копией или сырым указателем) определяется пользователем по документации C. Инструмент обеспечивает механическую корректность, пользователь обеспечивает семантическую корректность.
Компромиссы
Преимущества
- Автоматизация гарантии раскладки: раскладка прозрачных типов вычисляется механически для платформы, устраняя ошибки ручного дополнения/выравнивания
- Автоматическое определение разделения типов: неполные типы → дескрипторы, полные структуры → прозрачные типы
- Возможность аудита: вывод — обычный
.yx, пользователь может читать, изменять и помещать в систему контроля версий
Недостатки
- Владение по-прежнему требует ручного подтверждения: yx-bindgen не угадывает
.drop, не угадывает семантикуchar* - Зависимость от парсинга заголовочных файлов C: требуется libclang или tree-sitter-c
- Платформенная специфика: разные
--targetгенерируют разную раскладку, кроссплатформенные пакеты требуют нескольких версий или выбора во время выполнения
Стратегия реализации
- [ ] Парсинг заголовочных файлов C (libclang)
- [ ] Определение разделения типов (неполные типы vs полные структуры)
- [ ] Вычисление раскладки по ABI платформы (offset/size/align, по --target)
- [ ] Генерация кода (привязки библиотеки + типы + функции + методы)
- [ ] Интеграционное тестирование (sqlite3, libcurl, проверка раскладки на разных платформах)
Связь с другими RFC
- RFC-026 (родительский): Основной механизм FFI — сгенерированные привязки используют его синтаксис
Native.c("lib")("sym")и разделение типов - RFC-026a: Расширяемый механизм FFI — в будущем можно расширить для генерации привязок других механизмов типа
Native.wasm
Журнал проектных решений
| Решение | Определение | Причина | Дата |
|---|---|---|---|
| Позиционирование | Механический вывод черновика без угадывания владения | Владение определяется системой типов YaoXiang, пользователь подтверждает по документации C | 2026-07-03 |
| Гарантия раскладки | Механическое вычисление offset/size/align по --target | Устранение риска выхода за границы при ручной раскладке (RFC-026 §2.2) | 2026-07-03 |
| Определение типа | Неполные типы→дескрипторы, полные структуры→прозрачные типы | Соответствие разделению типов RFC-026 | 2026-07-03 |
Жизненный цикл и судьба
| Состояние | Расположение | Описание |
|---|---|---|
| Черновик | docs/design/rfc/draft/ | Зависит от предварительного внедрения RFC-026 |
| Принято | docs/design/rfc/accepted/ | Официальный проектный документ |
