Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

绑定全局函数

Github代码

本节介绍了如何绑定全局函数

例子

假设我们有一个简单的Add函数:

int Add(int a, int b) {
    return a + b; 
}

我们和绑定变量一样绑定他到全局对象。只是使用JS_NewCFunction创建JSValue

JSValue global_this = JS_GetGlobalObject(ctx);

constexpr int FnParamCount = 2;
JSValue fn = JS_NewCFunction(ctx, AddFnBinding, "Add", FnParamCount);

JS_SetPropertyStr(ctx, global_this, "Add", fn);

JS_FreeValue(ctx, global_this);

注意JS_NewCFunction的第二个参数AddFnBinding。他的类型是JSCFunction,一个函数指针:

typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);

QuickJS在看到JS调用C++函数时其实会调用我们给的这个函数指针。我们需要实现此函数指针并且在内部调用我们自己的C++函数:

JSValue AddFnBinding(JSContext* ctx, JSValue self, int argc,
                     JSValueConst* argv) {
    if (argc != 2) {
        return JS_ThrowPlainError(ctx, "Add function must has two parameters");
    }

    JSValueConst param1 = argv[0];
    JSValueConst param2 = argv[1];
    if (!JS_IsNumber(param1) || !JS_IsNumber(param2)) {
        return JS_ThrowTypeError(ctx, "Add accept two integral");
    }

    int32_t value1, value2;
    JS_ToInt32(ctx, &value1, param1);
    JS_ToInt32(ctx, &value2, param2);

    return JS_NewInt32(ctx, Add(value1, value2));
}

参数解释:

  • ctx:JS上下文

  • self:如果函数是类对象函数,那这里会传入类对象。否则是JS_UNDEFINED

  • argc:传给函数的参数格式

  • argv:传给函数的参数。注意这里写的虽是JSValueConst但并不代表他们是常量。因为底层定义如下:

    #define JSValueConst JSValue
    

这里首先检查参数个数和参数类型是否是我们想要的,然后将两个参数使用JS_ToXXX转换成C++类型,然后调用我们的Add函数并将结果穿给JSValue

更多函数类型

QuickJS内置了很多函数类型。JSCFunction只是最通用和最常见的类型。还有如下类型(定义在JSCFunctionEnum):

  • JS_CFUNC_generic:最通用的函数类型(就是我们使用的JSCFunction
  • JS_CFUNC_constructor:类的构造函数
  • 专门给数学函数定义的类型:
    • JS_CFUNC_f_f:形如double(*)(double)的函数指针
  • JS_CFUNC_f_f_f:形如double(*)(double, double)的函数指针
  • JS_CFUNC_getterJS_CFUNC_setter:getter和setter
  • JS_CFUNC_iterator_next:用于迭代器的类型
  • JS_CFUNC_constructor_or_func:<不清楚是什么,以后研究>
  • 以及某些函数的magic版本(在类型后面加_magic例如JS_CFUNC_generic_magic

这些函数的签名可以在JSCFunctionType看到:

typedef union JSCFunctionType {
    JSCFunction *generic;
    JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
    JSCFunction *constructor;
    JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic);
    JSCFunction *constructor_or_func;
    double (*f_f)(double);
    double (*f_f_f)(double, double);
    JSValue (*getter)(JSContext *ctx, JSValueConst this_val);
    JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val);
    JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic);
    JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic);
    JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val,
                             int argc, JSValueConst *argv, int *pdone, int magic);
} JSCFunctionType;

数学函数类型

JS_CFUNC_f_fJS_CFUNC_f_f_f

这两个函数都是接收double作为参数(f_f接收一个,f_f_f接收两个),返回一个double值。一般用于绑定sin,cos这种数学函数。优点是函数签名不含JSValue直接是double,不需要做额外的转换。

getter和setter

用于变量的Getter和Setter。可以使用这种方式实现只读变量(只实现getter不实现setter)。函数签名分别是:

  • Getter: JSValue (*getter)(JSContext *ctx, JSValueConst this_val)。传入要获得值的对象
  • Setter:JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val)。传入值的来源val和放入值的变量this_val

迭代器类型

<暂时没研究,之后补上>

带有magic的类型

几乎所有类型的函数都有magic版本。magic版本的作用是通过额外的参数int magic来将多个函数聚合在一起:

// binding function
JSValue BindMagicFn(JSContext* ctx, JSValue, int argc, JSValueConst* argv, int magic) {
    if (magic == 0) {
        MagicFn1();
    } else if (magic == 1) {
        MagicFn2();
    }

    return JS_UNDEFINED;
}

// bind
JSValue fn1 = JS_NewCFunctionMagic(ctx, BindMagicFn, "MagicFn1", FnParamCount, JS_CFUNC_generic_magic, 0);
JSValue fn2 = JS_NewCFunctionMagic(ctx, BindMagicFn, "MagicFn2", FnParamCount, JS_CFUNC_generic_magic, 1);

使用JS_NewCFunctionMagic来绑定magic函数。通过最后的参数来区分内部到底是在使用哪个函数。

如何绑定这些花里胡哨的函数类型

虽然函数类型和签名很多,但是JS_SetPropertyStr的第二个参数只接收JSCFunction类型。要如何绑定呢?有两种方法:

  1. 使用特定的绑定函数,比如magic相关的就是JS_NewCFunctionMagic,getter/setter就是 JS_DefinePropertyGetSet()

  2. 使用JSCFunctionType进行转换:

    JSCFunctionType fn_type;
    // JS_CFUNC_f_f_f pass two double elem and return one double elem
    fn_type.f_f_f = +[](double param1, double param2) -> double { return param1 + param2; };
    JSValue fn = JS_NewCFunction2(ctx, fn_type.generic, "Sum", 1, JS_CFUNC_f_f_f, 0);