V8LocalValueFromJsValue
v8::Localv8::Value 是 v8 和 Node-Api 中十分常见的一种类型。Local 创建了一个指向 js 对象的本地引用, 如下的代码可通过 V8LocalValueFromJsValue 函数从一个 js 对象中返回一个 Local 对象
1 | // src/js_native_api_v8.h |
刚开始看见这个代码比较疑惑, 因为对于一个没有经过 new 关键词生成的 local 实例, 其内存是分配在栈中, 类似于一个结构体
- 为何在离开 V8LocalValueFromJsValue 函数作用域后没有被自动释放内存, 返回一个结构体的函数是合法的吗?
- 还是因为这里是 inline 关键词的作用, 或许是因为 inline 是类似于宏定义文本替换才导致这样书写也是成功的?
验证
于是写了如下的 demo 开始验证, 当 getTest 函数返回一个结构体时会有如何的表现
1 |
|
最后程序是能正常运行的, 从运行结果来看, 两个 age 字段的地址是不同的
这样我大概得出了 getTest 函数中有一个临时性结构体 test,test 也确实会在 getTest 函数返回时被释放,但由于 test 被当做“值”进行返回,因此编译器将保证 getTest 的返回值对于 getTest 的调用者(caller)来说是有效的, 所以调用者 main 函数里面的 test 将得到一份被复制的数据, 于是表现出相同的 age 字段地址其实是不一样的
1 | ➜ c ./a.out |
那接下来继续再验证一下如果是 inline Test getTest() 的话, 两个字段的地址会不会是一样了 ?
答案是加上了 inline 后 age 字段地址还是不一样的。这样我开始明白了 inline 不是像宏定义那样进行的简单的文本替换, 于是单独学习了一下 inline 函数, 总结如下, 具体推荐阅读文章 C++ 内联函数 inline
- 宏是由预处理器对宏进行替换的,而内联函数是通过编译器控制实现的。
- 宏调用并不执行类型检查甚至连正常参数也不检查,但是函数调用却要检查。
- C语言的宏使用的是文本替换,可能导致无法预料的后果
- 在宏中的编译错误很难发现,因为它们引用的是扩展的代码,而不是程序员键入的
最后的验证, 如果 getTest 返回的是指针了 ?
1 |
|
由 demo 运行结果可见, age 字段的地址是一致的。说明平时我们在写代码时尽量不要传递结构体等实体, 因为将会花费一定的时间去复制数据, 而返回指针则会快捷很多
1 | ➜ c ./a.out |
到这里其实我还有最后一个疑惑的点, Local 创建了一个指向 js 对象的本地引用, 那么为何上面的 V8LocalValueFromJsValue 却是复制了一份数据而非引用关系了 ?
1 | // v8/include/v8-local-handle.h |
于是只能查看了一下 v8 中关于 Local 的定义, Local 有一个 val_ 属性, 是一个指针数据, 此时我猜测 V8LocalValueFromJsValue 函数中使用 memcpy 复制数据时, 如果遇见了指针类型, 只会复制一下地址, 所以新的 local 对象持有的 val_ 引用的是原 js 对象
1 |
|
于是写了上面的验证 demo, 运行结果也证实了 _val 值的是相等的
1 | ➜ c ./a.out |
小结
1 | // v8/include/v8-local-handle.h |
v8::Localv8::Value 既是非常常见也是非常重要的一个概念, 后面需要继续深入探究一下其实现与原理