Skip to content

扩展(C API) ​

扩展是向 JS 上下文添加全局对象和函数的原生 C 模块。它们实现 qz_ext_t 接口及其生命周期钩子。

qz_ext_t ​

c
typedef struct qz_ext_t {
    const char *name;
    int (*init)(qz_ext_t *ext, qz_t *rt);
    void (*destroy)(qz_ext_t *ext, qz_t *rt);
    int (*suspend)(qz_ext_t *ext, qz_t *rt);
    int (*resume)(qz_ext_t *ext, qz_t *rt);
    void *user_data;
} qz_ext_t;
字段描述
name用于诊断的人类可读名称
init在上下文创建时调用 — 注册 JS 全局对象,分配资源。成功返回 0,失败返回 <0。
destroy在上下文销毁时调用 — 释放扩展资源。JSContext 清理是自动的。
suspend在上下文挂起时调用 — 保存状态、暂停定时器、关闭连接。
resume在上下文恢复时调用 — 恢复状态、恢复定时器、重新打开连接。
user_data不透明的扩展状态。注意: 这在所有运行时之间共享 — 对于每个实例的数据,使用 config.host_data。

注册模型 ​

扩展在编译期通过 QZ_EXTENSIONS 宏注册(定义在 include/qzjs/qz_ext_registry.h 中)。没有运行时注册 API — 扩展集在 qzjs 库编译时固定。

c
// include/qzjs/qz_ext_registry.h
#define QZ_DEFAULT_EXTENSIONS \
    QZ_EXT_IF_WITH(COMPRESS,   &qz_compress_ext) \
    QZ_EXT_IF_WITH(CRYPTO_EXT, &qz_crypto_ext)   \
    QZ_EXT_IF_WITH(TEXTCODEC,  &qz_textcodec_ext) \
    QZ_EXT_IF_WITH(WAMR,       &qz_wamr_ext)

父项目通过在包含 qzjs 子目录之前覆盖 QZ_EXTENSIONS 来添加自定义扩展:

cmake
set(QZ_EXTENSIONS "QZ_DEFAULT_EXTENSIONS &my_extension")
set(QZ_EXTRA_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/my_extension.c)
add_subdirectory(deps/qzjs)

&my_extension 前用空格而非逗号:QZ_DEFAULT_EXTENSIONS 本身以尾随逗号 结尾,写成 "QZ_DEFAULT_EXTENSIONS, &my_extension" 会展开出空数组元素, 编译失败。

内置扩展 ​

扩展CMake 选项JS API
ext_compressQZ_WITH_COMPRESSgzip/zlib/deflate
ext_cryptoQZ_WITH_CRYPTO_EXTcrypto.subtle(SHA、HMAC、PBKDF2、AES-GCM)
ext_textcodecQZ_WITH_TEXTCODECTextEncoder、TextDecoder
ext_wamrQZ_WITH_WAMRWebAssembly(WAMR,默认)
ext_wasm3QZ_WITH_WASM3WebAssembly(wasm3,可选)

编写扩展 ​

c
#include <qzjs/qzjs.h>
#include <quickjs.h>

static int my_ext_init(qz_ext_t *ext, qz_t *rt) {
    JSContext *ctx = qz_get_jsctx(rt);
    if (!ctx) return -1;

    JSValue global = JS_GetGlobalObject(ctx);
    JS_SetPropertyStr(ctx, global, "hello",
        JS_NewCFunction(ctx, my_hello_fn, "hello", 0));
    JS_FreeValue(ctx, global);

    return 0;
}

static void my_ext_destroy(qz_ext_t *ext, qz_t *rt) {
    // 释放任何扩展特定资源
}

qz_ext_t my_extension = {
    .name = "my_extension",
    .init = my_ext_init,
    .destroy = my_ext_destroy,
    .suspend = NULL,
    .resume = NULL,
    .user_data = NULL,
};

每个运行时的数据 ​

qz_ext_t.user_data 在所有运行时之间共享。对于每个实例的状态,使用 config.host_data:

c
qz_config_t cfg = { .pal = pal, .host_data = my_per_rt_state };
qz_t *rt = qz_create(&cfg);

// 在扩展 init 内部:
my_state_t *st = (my_state_t *)qz_get_runtime_data(rt);

参见 ​

MIT 许可证