bun:ffi имеет экспериментальную поддержку компиляции и запуска C из JavaScript с низкими накладными расходами.
Использование (cc в bun:ffi)
Дополнительную информацию см. в вводном сообщении в блоге.
JavaScript:
import { cc } from "bun:ffi";
import source from "./hello.c" with { type: "file" };
const {
symbols: { hello },
} = cc({
source,
symbols: {
hello: {
args: [],
returns: "int",
},
},
});
console.log("Каков ответ на вселенную?", hello());Исходный код C:
int hello() {
return 42;
}Когда вы запустите hello.js, он напечатает:
bun hello.js
Каков ответ на вселенную? 42Под капотом cc использует TinyCC для компиляции кода C и затем связывает его со средой выполнения JavaScript, эффективно преобразуя типы на месте.
Примитивные типы
Те же значения FFIType, что и в dlopen, поддерживаются в cc.
FFIType | Тип C | Псевдонимы |
|---|---|---|
| cstring | char* | |
| function | (void*)(*)() | fn, callback |
| ptr | void* | pointer, void*, char* |
| i8 | int8_t | int8_t |
| i16 | int16_t | int16_t |
| i32 | int32_t | int32_t, int |
| i64 | int64_t | int64_t |
| i64_fast | int64_t | |
| u8 | uint8_t | uint8_t |
| u16 | uint16_t | uint16_t |
| u32 | uint32_t | uint32_t |
| u64 | uint64_t | uint64_t |
| u64_fast | uint64_t | |
| f32 | float | float |
| f64 | double | double |
| bool | bool | |
| char | char | |
| napi_env | napi_env | |
| napi_value | napi_value |
Строки, объекты и непримитивные типы
Чтобы упростить работу со строками, объектами и другими непримитивными типами, которые не отображаются 1:1 на типы C, cc поддерживает N-API.
Для передачи или получения значений JavaScript без каких-либо преобразований типов из функции C вы можете использовать napi_value.
Вы также можете передать napi_env для получения окружения N-API, используемого для вызова функции JavaScript.
Возврат строки C в JavaScript
Например, если у вас есть строка в C, вы можете вернуть её в JavaScript следующим образом:
import { cc } from "bun:ffi";
import source from "./hello.c" with { type: "file" };
const {
symbols: { hello },
} = cc({
source,
symbols: {
hello: {
args: ["napi_env"],
returns: "napi_value",
},
},
});
const result = hello();И в C:
#include <node/node_api.h>
napi_value hello(napi_env env) {
napi_value result;
napi_create_string_utf8(env, "Hello, Napi!", NAPI_AUTO_LENGTH, &result);
return result;
}Вы также можете использовать это для возврата других типов, таких как объекты и массивы:
#include <node/node_api.h>
napi_value hello(napi_env env) {
napi_value result;
napi_create_object(env, &result);
return result;
}Справочник cc
library: string[]
Массив library используется для указания библиотек, которые должны быть связаны с кодом C.
type Library = string[];
cc({
source: "hello.c",
library: ["sqlite3"],
});symbols
Объект symbols используется для указания функций и переменных, которые должны быть предоставлены JavaScript.
type Symbols = {
[key: string]: {
args: FFIType[];
returns: FFIType;
};
};source
source — это путь к файлу с кодом C, который должен быть скомпилирован и связан со средой выполнения JavaScript.
type Source = string | URL | BunFile;
cc({
source: "hello.c",
symbols: {
hello: {
args: [],
returns: "int",
},
},
});flags: string | string[]
flags — это необязательный массив строк, которые должны быть переданы компилятору TinyCC.
type Flags = string | string[];Это флаги, такие как -I для каталогов включения и -D для определений препроцессора.
define: Record<string, string>
define — это необязательный объект, которое должно быть передано компилятору TinyCC.
type Defines = Record<string, string>;
cc({
source: "hello.c",
define: {
NDEBUG: "1",
},
});Это определения препроцессора, передаваемые компилятору TinyCC.