bun:ffi prend en charge de manière expérimentale la compilation et l'exécution de code C depuis JavaScript avec une faible surcharge.
Utilisation (cc dans bun:ffi)
Consultez l'article de blog d'introduction pour plus d'informations.
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("Quelle est la réponse à l'univers ?", hello());Source C :
int hello() {
return 42;
}Lorsque vous exécutez hello.js, cela affichera :
bun hello.js
Quelle est la réponse à l'univers ? 42Sous le capot, cc utilise TinyCC pour compiler le code C puis le lier avec le runtime JavaScript, en convertissant efficacement les types sur place.
Types primitifs
Les mêmes valeurs FFIType que dans dlopen sont prises en charge dans cc.
FFIType | Type C | Alias |
|---|---|---|
| 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 |
Chaînes, objets et types non primitifs
Pour faciliter le travail avec les chaînes, les objets et autres types non primitifs qui ne correspondent pas 1:1 aux types C, cc prend en charge N-API.
Pour passer ou recevoir des valeurs JavaScript sans conversions de type depuis une fonction C, vous pouvez utiliser napi_value.
Vous pouvez également passer un napi_env pour recevoir l'environnement N-API utilisé pour appeler la fonction JavaScript.
Retourner une chaîne C vers JavaScript
Par exemple, si vous avez une chaîne en C, vous pouvez la retourner vers JavaScript comme ceci :
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();Et en 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;
}Vous pouvez également utiliser cela pour retourner d'autres types comme des objets et des tableaux :
#include <node/node_api.h>
napi_value hello(napi_env env) {
napi_value result;
napi_create_object(env, &result);
return result;
}Référence cc
library: string[]
Le tableau library est utilisé pour spécifier les bibliothèques qui doivent être liées avec le code C.
type Library = string[];
cc({
source: "hello.c",
library: ["sqlite3"],
});symbols
L'objet symbols est utilisé pour spécifier les fonctions et variables qui doivent être exposées à JavaScript.
type Symbols = {
[key: string]: {
args: FFIType[];
returns: FFIType;
};
};source
Le source est un chemin de fichier vers le code C qui doit être compilé et lié avec le runtime JavaScript.
type Source = string | URL | BunFile;
cc({
source: "hello.c",
symbols: {
hello: {
args: [],
returns: "int",
},
},
});flags: string | string[]
Le flags est un tableau optionnel de chaînes qui doit être passé au compilateur TinyCC.
type Flags = string | string[];Ce sont des indicateurs comme -I pour les répertoires d'inclusion et -D pour les définitions de préprocesseur.
define: Record<string, string>
Le define est un objet optionnel qui doit être passé au compilateur TinyCC.
type Defines = Record<string, string>;
cc({
source: "hello.c",
define: {
NDEBUG: "1",
},
});Ce sont des définitions de préprocesseur passées au compilateur TinyCC.