執行環境套件管理器打包器測試執行器程式碼檢查格式化指南參考部落格意見回饋

C 編譯器

使用低開銷從 JavaScript 編譯和執行 C 程式碼

bun:ffi 具有從 JavaScript 以低開銷編譯和執行 C 程式碼的實驗性支援。


使用方法(bun:ffi 中的 cc)

有關背景資訊,請參閱介紹性部落格文章。

JavaScript 程式碼:

hello.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("宇宙的答案是什麼?", hello());

C 原始碼:

hello.c
int hello() {
  return 42;
}

執行 hello.ts 會輸出:

terminal
bun hello.ts
What is the answer to the universe? 42

cc 使用 TinyCC 編譯 C 程式碼,然後將其與 JavaScript 執行環境連結,並在原地轉換型別。

基本型別

cc 支援與 dlopen 相同的 FFIType 值,但不包括 buffer_length。只有 cc 支援 napi_env 和 napi_value。

FFITypeC 型別別名
bufferchar*
cstringchar*
function(void*)(*)()fn, callback
ptrvoid*pointer, void*, char*
i8int8_tint8_t
i16int16_tint16_t
i32int32_tint32_t, int
i64int64_tint64_t, isize
i64_fastint64_t
u8uint8_tuint8_t
u16uint16_tuint16_t
u32uint32_tuint32_t
u64uint64_tuint64_t, usize
u64_fastuint64_t
f32floatfloat
f64doubledouble
boolbool
charchar
napi_envnapi_env
napi_valuenapi_value

字串、物件及非基本型別

對於無法與 C 型別一一對應的字串、物件和其他非基本型別,cc 支援 N-API。

使用 napi_value 可以在 C 函式中傳遞或接收 JavaScript 值,而無需進行任何型別轉換。

你也可以傳遞一個 napi_env,用以接收呼叫 JavaScript 函式時使用的 N-API 環境。

從 C 傳回字串到 JavaScript

例如,從 C 向 JavaScript 傳回字串:

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

對應的 C 程式碼:

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

同樣的方法也可以傳回物件和陣列等其他型別:

hello.c
#include <node/node_api.h>

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

cc 參考文件

library: string | string[]

使用 library 指定要與 C 程式碼連結的庫。

TypeScript
type Library = string | string[];

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

symbols

使用 symbols 物件來指定要暴露給 JavaScript 的函式和變數。

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

source

source 是要編譯並與 JavaScript 執行環境連結的 C 程式碼路徑。

TypeScript
type Source = string | URL | BunFile;

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

flags: string | string[]

flags 是傳遞給 TinyCC 編譯器的可選字串或字串陣列。

TypeScript
type Flags = string | string[];

這些是類似 -I 指定包含目錄和 -D 預處理定義的標誌。

define: Record<string, string>

define 是傳遞給 TinyCC 編譯器的可選前處理器定義物件。

TypeScript
type Defines = Record<string, string>;

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

停用 cc

傳遞 --no-ffi-cc 可為行程停用 C 編譯器。此後,任何對 cc() 的呼叫都會丟擲程式碼為 ERR_FFI_CC_DISABLED 的錯誤。--no-addons 標誌也會停用 cc(),此外還會停用 process.dlopen。

shell
bun --no-ffi-cc ./app.ts

Worker 會繼承其父級的設定。Worker 也可以使用 execArgv: ["--no-ffi-cc"] 為自身設定此選項。可以通過 bun build --compile --compile-exec-argv="--no-ffi-cc" 將此選項嵌入獨立執行檔。