VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

搜索

学习SpiderMonkey60的心得笔记(六)JS调用C/C++函数

已有 62 次阅读2020-3-19 15:03 |个人分类:JavaScript引擎

学习JS调用C/C++函数调用,下面这篇文章对我帮助非常大,第一篇的地址如下,作者一连发了4篇连续文章
https://blog.csdn.net/zxhoo/article/details/8986593

JS调用C/C++函数,其形式为这样:c/c++一个函数,如:
        /* C/C++ */
        int max(a,b){return a>b ? a: b;}
 
而在JS中没有定义这样的函数却直接应用。如下:
        /* JavaScript */
        var a = 123;
        var b = 234;
        var v = max(a, b);

做到让c/c++的函数,在JS中可以直接调用,必须要完成如下步骤:
        步骤1: c/++中实现函数
        步骤2: SpiderMonkey中要有一个和C/C++实现的函数相对应的函数,称为JSNative函数
        步骤3: 让c/C++的函数在JS可见(即,JS能调用)

步骤1:
无须赘述,大家都明白的,也就是上面那个method。为了演示的便捷,我们不妨将这个函数设成是静态的。
        static int max(int a, int b) {
               return ((a >= b) ? a : b);
        }

步骤2:
SpiderMonkey中要有一个和C/C++实现的函数相对应的函数,称为JSNative本地函数。在js目录的CallArgs.h文件里:
         /* Typedef for native functions called by the JS VM. */
         typedef bool (*JSNative)(JSContext* cx, unsigned argc, JS::Value* vp);

SpiderMonkey为这类函数提供了统一的函数签名,也即定义了一个函数指针,形式如下:
         bool
         (*JSNative)(JSContext* cx, unsigned argc, JS::Value* vp);

其中,cx是我们已经熟悉的了;argc则是这个本地函数的参数个数;vp就是具体的参数。
在前面的JS代码里,argc就是2,vp就是a和b,分别为123和234,但是,我们该如何获得这些数值呢?
SpiderMonkey为JSNative的参数专门定义了一个类CallArgs(前面提到的头文件),这个类的两个私有成员变量分别为:
          JS::Value* argv;
          unsigned argc;

这个argc就是参数个数,指针argv就是指向具体参数数组。同时有一个JSAPI获取这些数据:
          CallArgs
          CallArgsFromVp(unsigned argc, Value* vp);

因此,我们可以定义一个CallArgs变量(类),利用上面的JSAPI接收函数的参数,代码如下:
         //JSNative 函数取名:js_max
         static bool js_max(JSContext* cx, unsigned argc, JS::Value *vp) {
                //定义变量args接收函数的参数
                JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
                //CallArgs类有提取参数值的方法,其中[]直接提取vp的数值
                //调用c/c++代码,计算函数的结果
                //下面代码中用到了前面已经学习过的数据类型转换,具体args[0].toInt32(),就是把第一个参数转成int
                int c = max(args[0].toInt32(), args[1].toInt32());
                printf("the c is: %d\n", c);
                //结果返回,用到CallArgs类的另外一个方法.rval(),这个方法就是返回函数值。
                //这里再次用到数据类型转换,因上一行代码是c/c++函数,现在的返回值是JS类型的,两者间必须转换
                args.rval().setInt32(c);
                return true;
         }

步骤3:
让C/C++函数在JS中成为可以直接调用的函数,则要让该函数在JS中可见。下面的原文来自(以前笔记中提到的)手册:
 /*
  * Add this to your JSContext setup code.
  * This makes your C function visible as a global function in JavaScript.
  */
          if (!JS_DefineFunction(cx, global, "justForFun", &justForFun, 0, 0))
                 return false;
To define many JSAPI functions at once, use JS_DefineFunctions.
让函数在JS可见的JSAPI有对单个函数和一群函数的,它们为:
          bool
          JS_DefineFunctions(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* fs);
          JSFunction*
          JS_DefineFunction(JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
                                     JSNative call, unsigned nargs, unsigned attrs);
          JSFunction*
          JS_DefineUCFunction(JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
                                         size_t namelen, JSNative call, unsigned nargs, unsigned attrs);
           JSFunction*
           JS_DefineFunctionById(JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                                             JSNative call, unsigned nargs, unsigned attrs);

上面那些JSAPI中的第一个是针对一群函数的,后面三个是对单个函数的。由于一群函数可塑性大,我们这里用来这个练习
           bool
           JS_DefineFunctions(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* fs);

这个函数中cx和obj都是熟悉的,最后的参数就是一群函数的列表。
其中,JSFunctionSpec是SpiderMonkey定义的一个结构
           struct JSFunctionSpec {
                  const char* name;
                  JSNativeWrapper call;
                  uint16_t nargs;
                  uint16_t flags;
                  const char* selfHostedName;
          };
 
并且,配合该结构使用,还定义了一些宏,其中两个常用的如下。
         #define JS_FS_END JS_FN(nullptr, nullptr, 0, 0)
         #define JS_FN(name, call, nargs, flags) JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
 
当我们使用让一群函数在JS中可见时,要先定义一个上面的结构,再使用上面的JSAPI,代码如下:
         static JSFunctionSpec myjs_global_functions[] = {
                /* #define JS_FS_END JS_FN(nullptr, nullptr, 0, 0) in jsapi.h*/
                //max就是JS可见的函数,js_max就是SpiderMonkey中的JSNative函数,2是参数个数,0是flags标志
                JS_FN("max", js_max, 2, 0),
                JS_FS_END
         };

然后在程序中使用上面的结构,让c/c++的函数在JS中可见(代码)
         if (!JS_DefineFunctions(cx, global, myjs_global_functions)) {
                 printf(" here is error!");
                 return false;
         }
         ok = JS_GetProperty(cx, obj, "num", &rval);
         if (ok) {
                 printf("The num is %d\n", rval.toInt32());
         }
         else {
                 printf("The num isn't found!");
                 return false;
         }
 
程序输出结果是:
          the c is: 234
         The num is 234

1R2aIKW



附录:flags(in vm/JSFunction.h)
  enum Flags {
    INTERPRETED = 0x0001, /* function has a JSScript and environment. */
    CONSTRUCTOR = 0x0002, /* function that can be called as a constructor */
    EXTENDED = 0x0004,    /* structure is FunctionExtended */
    BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */
    WASM_OPTIMIZED = 0x0010,   /* asm.js/wasm function that has a jit entry */
    HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a
                                  name was guessed for it anyway */
    HAS_BOUND_FUNCTION_NAME_PREFIX =
        0x0020, /* bound functions reuse the HAS_GUESSED_ATOM
                   flag to track if atom_ already contains the
                   "bound " function name prefix */
    LAMBDA =
        0x0040, /* function comes from a FunctionExpression, ArrowFunction, or
                   Function() call (not a FunctionDeclaration or nonstandard
                   function-statement) */
    SELF_HOSTED = 0x0080, /* function is self-hosted builtin and must not be
                             decompilable nor constructible. */
    HAS_COMPILE_TIME_NAME = 0x0100, /* function had no explicit name, but a
                                       name was set by SetFunctionName
                                       at compile time */
    INTERPRETED_LAZY =
        0x0200, /* function is interpreted but doesn't have a script yet */
    RESOLVED_LENGTH =
        0x0400,             /* f.length has been resolved (see fun_resolve). */
    RESOLVED_NAME = 0x0800, /* f.name has been resolved (see fun_resolve). */
    FUNCTION_KIND_SHIFT = 13,
    FUNCTION_KIND_MASK = 0x7 << FUNCTION_KIND_SHIFT,
    ASMJS_KIND = AsmJS << FUNCTION_KIND_SHIFT,
    ARROW_KIND = Arrow << FUNCTION_KIND_SHIFT,
    METHOD_KIND = Method << FUNCTION_KIND_SHIFT,
    CLASSCONSTRUCTOR_KIND = ClassConstructor << FUNCTION_KIND_SHIFT,
    GETTER_KIND = Getter << FUNCTION_KIND_SHIFT,
    SETTER_KIND = Setter << FUNCTION_KIND_SHIFT,
    /* Derived Flags values for convenience: */
    NATIVE_FUN = 0,
    NATIVE_CTOR = NATIVE_FUN | CONSTRUCTOR,
    NATIVE_CLASS_CTOR = NATIVE_FUN | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND,
    ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR,
    ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA,
    WASM_FUN = NATIVE_FUN | WASM_OPTIMIZED,
    INTERPRETED_METHOD = INTERPRETED | METHOD_KIND,
    INTERPRETED_METHOD_GENERATOR_OR_ASYNC = INTERPRETED | METHOD_KIND,
    INTERPRETED_CLASS_CONSTRUCTOR =
        INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR,
    INTERPRETED_GETTER = INTERPRETED | GETTER_KIND,
    INTERPRETED_SETTER = INTERPRETED | SETTER_KIND,
    INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR,
    INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND,
    INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = INTERPRETED | LAMBDA,
    INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR,
    INTERPRETED_GENERATOR_OR_ASYNC = INTERPRETED,
    NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
    STABLE_ACROSS_CLONES = CONSTRUCTOR | LAMBDA | SELF_HOSTED |
                           HAS_COMPILE_TIME_NAME | FUNCTION_KIND_MASK
  };

路过

雷人

握手

鲜花

鸡蛋

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 加入驿站

关闭

站长提醒上一条 /2 下一条

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号 )|网站地图wx_jqr

GMT+8, 2020-10-27 10:38

Powered by CcTry.CoM

© 2009-2020 cctry.com

返回顶部