Skip to content

bun:ffi tem suporte experimental para compilar e executar C a partir de JavaScript com baixa sobrecarga.


Uso (cc em bun:ffi)

Veja o post de introdução do blog para mais informações.

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("Qual é a resposta para o universo?", hello());

Código C:

c
int hello() {
  return 42;
}

Quando você executar hello.js, ele imprimirá:

sh
bun hello.js
Qual é a resposta para o universo? 42

Internamente, cc usa TinyCC para compilar o código C e depois linká-lo com o runtime JavaScript, convertendo eficientemente os tipos in-place.

Tipos primitivos

Os mesmos valores FFIType em dlopen são suportados em cc.

FFITypeTipo CAliases
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

Strings, objetos e tipos não primitivos

Para facilitar o trabalho com strings, objetos e outros tipos não primitivos que não mapeiam 1:1 para tipos C, cc suporta N-API.

Para passar ou receber valores JavaScript sem nenhuma conversão de tipo de uma função C, você pode usar napi_value.

Você também pode passar um napi_env para receber o ambiente N-API usado para chamar a função JavaScript.

Retornando uma string C para JavaScript

Por exemplo, se você tiver uma string em C, pode retorná-la para JavaScript assim:

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

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

Você também pode usar isso para retornar outros tipos como objetos e arrays:

c
#include <node/node_api.h>

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

Referência cc

library: string[]

O array library é usado para especificar as bibliotecas que devem ser linkadas com o código C.

ts
type Library = string[];

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

symbols

O objeto symbols é usado para especificar as funções e variáveis que devem ser expostas ao JavaScript.

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

source

O source é um caminho de arquivo para o código C que deve ser compilado e linkado com o runtime JavaScript.

ts
type Source = string | URL | BunFile;

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

flags: string | string[]

O flags é um array opcional de strings que devem ser passadas para o compilador TinyCC.

ts
type Flags = string | string[];

Estas são flags como -I para diretórios de include e -D para definições de preprocessor.

define: Record<string, string>

O define é um objeto opcional que deve ser passado para o compilador TinyCC.

ts
type Defines = Record<string, string>;

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

Estas são definições de preprocessor passadas para o compilador TinyCC.

Bun by www.bunjs.com.cn edit