Skip to content

bun:ffi tiene soporte experimental para compilar y ejecutar C desde JavaScript con baja sobrecarga.


Uso (cc en bun:ffi)

Consulta la entrada de blog de introducción para obtener más información.

JavaScript:

ts
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("¿Cuál es la respuesta al universo?", hello());

Fuente C:

c
int hello() {
  return 42;
}

Cuando ejecutes hello.js, imprimirá:

sh
bun hello.js
¿Cuál es la respuesta al universo? 42

Bajo el capó, cc usa TinyCC para compilar el código C y luego enlazarlo con el runtime de JavaScript, convirtiendo eficientemente los tipos en su lugar.

Tipos primitivos

Los mismos valores FFIType en dlopen son compatibles en cc.

FFITypeTipo CAlias
cstringchar*
function(void*)(*)()fn, callback
ptrvoid*pointer, void*, char*
i8int8_tint8_t
i16int16_tint16_t
i32int32_tint32_t, int
i64int64_tint64_t
i64_fastint64_t
u8uint8_tuint8_t
u16uint16_tuint16_t
u32uint32_tuint32_t
u64uint64_tuint64_t
u64_fastuint64_t
f32floatfloat
f64doubledouble
boolbool
charchar
napi_envnapi_env
napi_valuenapi_value

Cadenas, objetos y tipos no primitivos

Para facilitar el trabajo con cadenas, objetos y otros tipos no primitivos que no se mapean 1:1 a tipos C, cc soporta N-API.

Para pasar o recibir valores de JavaScript sin ninguna conversión de tipo desde una función C, puedes usar napi_value.

También puedes pasar un napi_env para recibir el entorno N-API usado para llamar a la función de JavaScript.

Devolver una cadena C a JavaScript

Por ejemplo, si tienes una cadena en C, puedes devolverla a JavaScript así:

ts
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();

Y en C:

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;
}

También puedes usar esto para devolver otros tipos como objetos y arrays:

c
#include <node/node_api.h>

napi_value hello(napi_env env) {
  napi_value result;
  napi_create_object(env, &result);
  return result;
}

Referencia de cc

library: string[]

El array library se usa para especificar las bibliotecas que deben enlazarse con el código C.

ts
type Library = string[];

cc({
  source: "hello.c",
  library: ["sqlite3"],
});

symbols

El objeto symbols se usa para especificar las funciones y variables que deben exponerse a JavaScript.

ts
type Symbols = {
  [key: string]: {
    args: FFIType[];
    returns: FFIType;
  };
};

source

El source es una ruta de archivo al código C que debe compilarse y enlazarse con el runtime de JavaScript.

ts
type Source = string | URL | BunFile;

cc({
  source: "hello.c",
  symbols: {
    hello: {
      args: [],
      returns: "int",
    },
  },
});

flags: string | string[]

El flags es un array opcional de cadenas que deben pasarse al compilador TinyCC.

ts
type Flags = string | string[];

Estas son banderas como -I para directorios de inclusión y -D para definiciones de preprocesador.

define: Record<string, string>

El define es un objeto opcional que debe pasarse al compilador TinyCC.

ts
type Defines = Record<string, string>;

cc({
  source: "hello.c",
  define: {
    NDEBUG: "1",
  },
});

Estas son definiciones de preprocesador pasadas al compilador TinyCC.

Bun por www.bunjs.com.cn editar