C 編譯器
使用低開銷從 JavaScript 編譯和執行 C 程式碼
bun:ffi 具有從 JavaScript 以低開銷編譯和執行 C 程式碼的實驗性支援。
使用方法(bun:ffi 中的 cc)
有關背景資訊,請參閱介紹性部落格文章。
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("宇宙的答案是什麼?", hello());
C 原始碼:
int hello() {
return 42;
}
執行 hello.ts 會輸出:
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。
FFIType | C 型別 | 別名 |
|---|---|---|
| buffer | char* | |
| 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, isize |
| i64_fast | int64_t | |
| u8 | uint8_t | uint8_t |
| u16 | uint16_t | uint16_t |
| u32 | uint32_t | uint32_t |
| u64 | uint64_t | uint64_t, usize |
| u64_fast | uint64_t | |
| f32 | float | float |
| f64 | double | double |
| bool | bool | |
| char | char | |
| napi_env | napi_env | |
| napi_value | napi_value |
字串、物件及非基本型別
對於無法與 C 型別一一對應的字串、物件和其他非基本型別,cc 支援 N-API。
使用 napi_value 可以在 C 函式中傳遞或接收 JavaScript 值,而無需進行任何型別轉換。
你也可以傳遞一個 napi_env,用以接收呼叫 JavaScript 函式時使用的 N-API 環境。
從 C 傳回字串到 JavaScript
例如,從 C 向 JavaScript 傳回字串:
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 程式碼:
#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;
}
同樣的方法也可以傳回物件和陣列等其他型別:
#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 程式碼連結的庫。
type Library = string | string[];
cc({
source: "hello.c",
library: ["sqlite3"],
});
symbols
使用 symbols 物件來指定要暴露給 JavaScript 的函式和變數。
type Symbols = {
[key: string]: {
args: FFIType[];
returns: FFIType;
};
};
source
source 是要編譯並與 JavaScript 執行環境連結的 C 程式碼路徑。
type Source = string | URL | BunFile;
cc({
source: "hello.c",
symbols: {
hello: {
args: [],
returns: "int",
},
},
});
flags: string | string[]
flags 是傳遞給 TinyCC 編譯器的可選字串或字串陣列。
type Flags = string | string[];
這些是類似 -I 指定包含目錄和 -D 預處理定義的標誌。
define: Record<string, string>
define 是傳遞給 TinyCC 編譯器的可選前處理器定義物件。
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。
bun --no-ffi-cc ./app.ts
Worker 會繼承其父級的設定。Worker 也可以使用 execArgv: ["--no-ffi-cc"] 為自身設定此選項。可以通過 bun build --compile --compile-exec-argv="--no-ffi-cc" 將此選項嵌入獨立執行檔。