Skip to content

RFC-026b: Инструментарий yx-bindgen

Родительский RFC: RFC-026: Основной механизм FFI

Зависимость: Реализация данного RFC зависит от предварительного внедрения основного механизма FFI из RFC-026.

Аннотация

yx-bindgen механически преобразует заголовочные файлы C в файлы FFI-привязок .yx, генерируя привязки библиотек, разделение типов (непрозрачные дескрипторы / прозрачные типы), объявления функций и привязки методов.

Два основных принципа:

  1. Механический вывод черновика без угадывания владения — владение определяется системой типов YaoXiang, пользователь подтверждает по документации C
  2. Гарантия правильной раскладки для платформы — размеры полей и выравнивание прозрачных типов вычисляются для целевой платформы, обеспечивая бинарное соответствие структурам C

Мотивация

Ручное написание FFI-привязок утомительно и подвержено ошибкам — библиотеки C содержат десятки или сотни функций. Ещё опаснее раскладка прозрачных типов: при ручном написании Timespec: Type = { tv_sec: Int64, tv_nsec: Int64 } размеры полей, выравнивание и дополнение должны точно соответствовать структуре C struct timespec целевой платформы, иначе C будет читать/записывать по неправильной раскладке, что приведёт к выходу за границы (гарантия доверия из RFC-026 §2.2). yx-bindgen механически вычисляет раскладку из .h-файлов, устраняя этот риск ручной гарантии.

Предложение

1. Использование

bash
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 целевой платформы:

c
struct timespec {
    time_t tv_sec;    // платформо-зависимо: Linux x86_64 = 8 байт
    long   tv_nsec;   // 8 байт
};
yaoxiang
// Целевая платформа 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):

c
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):

yaoxiang
// 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.close

6. Корректировка пользователем

Сгенерированные привязки — черновик, пользователь подтверждает семантику владения по документации библиотеки C (yx-bindgen не угадывает):

yaoxiang
// Сгенерировано: 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. Инструмент обеспечивает механическую корректность, пользователь обеспечивает семантическую корректность.


Компромиссы

Преимущества

  1. Автоматизация гарантии раскладки: раскладка прозрачных типов вычисляется механически для платформы, устраняя ошибки ручного дополнения/выравнивания
  2. Автоматическое определение разделения типов: неполные типы → дескрипторы, полные структуры → прозрачные типы
  3. Возможность аудита: вывод — обычный .yx, пользователь может читать, изменять и помещать в систему контроля версий

Недостатки

  1. Владение по-прежнему требует ручного подтверждения: yx-bindgen не угадывает .drop, не угадывает семантику char*
  2. Зависимость от парсинга заголовочных файлов C: требуется libclang или tree-sitter-c
  3. Платформенная специфика: разные --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, пользователь подтверждает по документации C2026-07-03
Гарантия раскладкиМеханическое вычисление offset/size/align по --targetУстранение риска выхода за границы при ручной раскладке (RFC-026 §2.2)2026-07-03
Определение типаНеполные типы→дескрипторы, полные структуры→прозрачные типыСоответствие разделению типов RFC-0262026-07-03

Жизненный цикл и судьба

СостояниеРасположениеОписание
Черновикdocs/design/rfc/draft/Зависит от предварительного внедрения RFC-026
Принятоdocs/design/rfc/accepted/Официальный проектный документ