bun:ffi ha supporto sperimentale per compilare ed eseguire C da JavaScript con basso overhead.
Utilizzo (cc in bun:ffi)
Vedi il post del blog introduttivo per maggiori informazioni.
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("Qual è la risposta all'universo?", hello());Sorgente C:
int hello() {
return 42;
}Quando esegui hello.js, stamperà:
bun hello.js
Qual è la risposta all'universo? 42Sotto il cofano, cc usa TinyCC per compilare il codice C e poi linkarlo con il runtime JavaScript, convertendo efficientemente i tipi in-place.
Tipi primitivi
Gli stessi valori FFIType in dlopen sono supportati in cc.
FFIType | Tipo 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 |
Stringhe, oggetti e tipi non primitivi
Per rendere più facile lavorare con stringhe, oggetti e altri tipi non primitivi che non mappano 1:1 ai tipi C, cc supporta N-API.
Per passare o ricevere valori JavaScript senza alcuna conversione di tipo da una funzione C, puoi usare napi_value.
Puoi anche passare un napi_env per ricevere l'ambiente N-API usato per chiamare la funzione JavaScript.
Restituire una stringa C a JavaScript
Ad esempio, se hai una stringa in C, puoi restituirla a JavaScript in questo modo:
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();E in 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;
}Puoi anche usare questo per restituire altri tipi come oggetti e array:
#include <node/node_api.h>
napi_value hello(napi_env env) {
napi_value result;
napi_create_object(env, &result);
return result;
}Riferimento cc
library: string[]
L'array library è usato per specificare le librerie che dovrebbero essere linkate con il codice C.
type Library = string[];
cc({
source: "hello.c",
library: ["sqlite3"],
});symbols
L'oggetto symbols è usato per specificare le funzioni e le variabili che dovrebbero essere esposte a JavaScript.
type Symbols = {
[key: string]: {
args: FFIType[];
returns: FFIType;
};
};source
Il source è un percorso file al codice C che dovrebbe essere compilato e linkato con il runtime JavaScript.
type Source = string | URL | BunFile;
cc({
source: "hello.c",
symbols: {
hello: {
args: [],
returns: "int",
},
},
});flags: string | string[]
I flags sono un array opzionale di stringhe che dovrebbero essere passate al compilatore TinyCC.
type Flags = string | string[];Questi sono flag come -I per le directory di include e -D per le definizioni del preprocessore.
define: Record<string, string>
Il define è un oggetto opzionale che dovrebbe essere passato al compilatore TinyCC.
type Defines = Record<string, string>;
cc({
source: "hello.c",
define: {
NDEBUG: "1",
},
});Queste sono definizioni del preprocessore passate al compilatore TinyCC.