QuickJS嵌入式开发:将C函数注册为JavaScript回调函数

QuickJS嵌入式开发:将C函数注册为JavaScript回调函数
最新回答
峩只是忘不掉回忆

2026-10-18 16:05:26

在QuickJS嵌入式开发中,将C函数注册为JavaScript回调函数的核心步骤包括定义C函数、创建包装器、注册到运行时,并通过JavaScript调用。以下是详细说明:

1. 定义核心C回调函数

需实现一个符合QuickJS回调签名的函数,处理JavaScript传递的参数并返回结果。

  • 函数签名:

    static JSValue myCallback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)

    参数:

    ctx:QuickJS上下文,用于操作JS值或抛出异常。

    this_val:调用时的this值(通常忽略)。

    argc/argv:参数数量及数组(JS值类型)。

    返回值:JSValue类型,可通过JS_NewString、JS_NewInt32等构造,无返回值时返回JS_UNDEFINED。

  • 示例逻辑:

    static JSValue myCallback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { if (argc > 0 && JS_IsString(argv[0])) { const char *str = JS_ToCString(ctx, argv[0]); printf("Received string: %sn", str); JS_FreeCString(ctx, str); // 释放字符串内存 } return JS_NewString(ctx, "Response from C");}
2. 创建包装器函数

QuickJS的JS_NewCFunction要求特定签名,需通过包装器适配核心函数。

  • 包装器示例:static JSValue js_myCallback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { return myCallback(ctx, this_val, argc, argv); // 直接调用核心函数}
3. 注册C函数到QuickJS运行时

将包装器绑定到全局对象或特定对象,使其在JavaScript中可访问。

  • 关键API:

    JS_GetGlobalObject(ctx):获取全局对象(如window)。

    JS_NewCFunction(ctx, func, name, nargs):创建C函数对象。

    func:包装器函数指针。

    name:JavaScript中暴露的函数名。

    nargs:期望参数数量(设为0表示不强制检查)。

    JS_SetPropertyStr(ctx, obj, name, val):将函数绑定到对象属性。

  • 完整注册代码:

    void registerMyCallback(JSContext *ctx) { JSValue global_obj = JS_GetGlobalObject(ctx); JSValue func_obj = JS_NewCFunction(ctx, js_myCallback, "myCallback", 0); JS_SetPropertyStr(ctx, global_obj, "myCallback", func_obj); JS_FreeValue(ctx, global_obj); // 释放全局对象引用}
4. 在JavaScript中调用C函数

注册后,可直接像普通JS函数一样调用,并传递参数:

myCallback(); // 无参调用myCallback("Hello", 42); // 传递字符串和数字关键考量与最佳实践
  • 参数验证与类型检查:使用JS_IsNumber、JS_IsString等验证参数类型,避免未定义行为。

    if (argc < 1 || !JS_IsString(argv[0])) { return JS_ThrowTypeError(ctx, "Expected a string argument");}
  • 错误处理:通过JS_ThrowTypeError或JS_ThrowInternalError抛出异常,供JS捕获:

    if (error_condition) { return JS_ThrowInternalError(ctx, "C function failed");}
  • 内存管理:

    对JS_ToCString等返回的字符串需调用JS_FreeCString。

    长期持有JSValue时使用JS_DupValue增加引用计数,不再需要时JS_FreeValue。

  • 上下文隔离:确保操作在正确的JSContext中执行,避免跨上下文访问。

  • 编译链接:包含头文件quickjs.h,并链接libquickjs.a或动态库。

完整示例代码#include "quickjs.h"// 核心回调函数static JSValue myCallback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { if (argc > 0 && JS_IsString(argv[0])) { const char *str = JS_ToCString(ctx, argv[0]); printf("C received: %sn", str); JS_FreeCString(ctx, str); } return JS_NewString(ctx, "C response");}// 包装器static JSValue js_myCallback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { return myCallback(ctx, this_val, argc, argv);}// 注册函数void registerCallbacks(JSContext *ctx) { JSValue global = JS_GetGlobalObject(ctx); JS_SetPropertyStr(ctx, global, "myCallback", JS_NewCFunction(ctx, js_myCallback, "myCallback", 0)); JS_FreeValue(ctx, global);}int main() { JSRuntime *rt = JS_NewRuntime(); JSContext *ctx = JS_NewContext(rt); registerCallbacks(ctx); // 执行JS代码调用C函数 const char *script = "console.log(myCallback('Test'));"; JS_Eval(ctx, script, strlen(script), "<inline>", JS_EVAL_GLOBAL); JS_FreeContext(ctx); JS_FreeRuntime(rt); return 0;}总结

通过定义C函数、创建包装器、注册到QuickJS运行时,并遵循参数验证、错误处理和内存管理规范,可实现高效的C-JS交互。此机制是嵌入式开发中扩展JavaScript功能、处理底层操作的关键技术。