绑定全局变量
我们首先从最简单的全局变量绑定开始。
QuickJS的类型
QuickJS中使用JSValue包装所有C/C++类型。支持的类型如下:
- Number:数字类型
- String:字符串类型(是C风格字符串不是
std::string) - Boolean:布尔值
- Class:类
- Module:模块
- Exception:异常
- Function:函数
- Array:数组
例子
现在希望绑定一个数值类型的变量:
| |
做法如下:
| |
C++和JS的所有数据交换都是通过JSValue进行的。这里步骤如下:
- 首先使用
JS_NewInt32创建一个整数类型的JSValue - 做异常检查
- 得到全局对象
JS_GetGlobalObject - 使用
JS_SetPropertyStr将我们的变量注册到全局对象中 - 清理内存
重点是注册的函数JS_SetPropertyStr。其原型如下:
| |
this_object:要注册到的对象prop:要注册的对象名称(在JS中使用的名称)val:要注册的对象
注册完之后就可以在JS中使用了:
| |
绑定基础类型变量的规则
绑定对象的步骤如下:
- 使用
JS_NewXXX来创建一个JS对象 - 使用
JS_SetPropertyStr来将对象绑定在另一个JS对象中 - 清理内存
可创建的JS对象
- 通用的数值类创建使用
JS_NewNumber。细分如下:JS_NewInt32:创建32位的整数JS_NewInt64:创建64位整数(底层规则是:如果是32位,调用JS_NewInt32,否则直接调用JS_NewFloat64存在64位浮点数中)JS_NewUint32:创建无符号32位整数(规则通JS_NewInt64)JS_NewFloat64:创建double类型(JS中没有float类型都是double)
- 大数类型:
JS_NewBigInt64JS_NewBigUInt64
JS_NewBool:创建布尔值JS_NewClass:创建类JS_NewObject:创建对象- 字符串类:
JS_NewStringJS_NewStringLen:可指定字符串长度
何时释放JSValue?
JSValue的释放也是有讲究的。其底层实现为:
| |
是先判断是否有引用计数,如果有的话,当计数降为0释放内存。
有引用计数的一般是类和对象类型。像基础的数值类型是直接值拷贝入JSValue的,也不会有内存分配/释放:
| |
tag:即JS_TAG_XXX类型,标识JSValue的类型u:如果是数值类型,就记录在非ptr中并且没有内存分配。否则进行内存分配,将指针记录在ptr中
而JS_VALUE_HAS_REF_COUNT也让我们知道哪些是会进行内存分配的:
| |
那么所有时候,只要使用了JS_NewXXX就一定要调用JS_FreeValue吗?答案是否定的。具体是否需要释放要看之后的函数是否影响了其引用计数。比如上面HelloWorld中的代码我们就没有释放new_obj。因为new_obj被创建出来时引用计数是1。而JS_SetPropertyStr传入new_obj后是不会改变其引用计数的。这个时候如果调用了JS_FreeValue则会将new_obj的引用计数降为0,进而释放内存。但此时其已被注册在global_this中了。在JSRuntime释放的时候会尝试释放所有被引用的节点,这个时候会再次释放new_obj造成程序崩溃。
所以是否要释放JSValue,关键在于之后使用的函数是否增加了JSValue的引用计数。如果增加了就需要释放。
或者说的更严谨一点,当其他函数内部使用了js_dup()时就是增加了引用计数,这个时候我们就得手动释放。
绑定类对象
如果想要绑定类对象,需要使用JS_NewClassObject并且将类对象设置进去:
| |
这里需要一个class_id,class_id是类的唯一标识,在你注册类的时候会生成一个(等待后面绑定类的时候会说到)。
然后使用JS_SetOpaque函数将我们的类对象塞给JSValue即可。
绑定空对象
使用JS_NewObject创建一个空对象(即创建一个JS中的Object实例)。这个对象不和任何类相关联,所以也不需要一个class_id。
内置JSValue常量
JS_UNDEFINEDJS_NULL
这两个是内置的字面常量,可以直接使用无需JS_NewXXX。