看源码的时候就看 8.3.100 吧…

文档

编译步骤

# https://v8.dev/docs/source-code#using-git
# https://v8.dev/docs/embed
# 安装 google depot tools
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=$PATH:/path/to/depot_tools
mkdir v8
cd v8
# 拉取 V8 源码
fetch v8
# 可选, 切换到一个可用的分支, lkgr = Last Known Good Revision
git checkout lkgr
# 同步依赖
gclient sync
# 配置 release 版本编译参数
tools/dev/v8gen.py x64.release.sample
# 编译, v8_monolith 表示直接将所有的动态库链接起来
ninja -C out.gn/x64.release.sample v8_monolith
 
# 配置和编译 debug 版本
# 可以参考 https://mem2019.github.io/jekyll/update/2019/07/18/V8-Env-Config.html
# 2020-3-19: debug 版本没编过, 有 wasm 的 unit test 错误...
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug
# 2020-3-20
# 可以通过 gn args out.gn/x64.release.sample 后修改里面的 debug flag 实现

如果要带着 V8 编译自己的代码, 可以参照以下 CmakeLists.txt

cmake_minimum_required(VERSION 3.2)
project(V8Demo)
# 指定 V8 的头文件地址
include_directories(
  /Users/bopeng/c/tools/v8/include
)
# 指定 lib 的搜索目录
link_directories(
  /Users/bopeng/c/tools/v8/out.gn/x64.release.sample/obj
)
# V8 编译的结果就是这个 .a 文件
link_libraries(v8_monolith)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -pthread")
set(SOURCE_FILES ./main.cc)
add_executable(Demo ${SOURCE_FILES})

基本概念

isolate

隔离区, 一个 isolate 对应一个 V8 实例, 一个 isolate 下可以有多个 context

handle

V8 中所有对象(context, 数据, 模板)都通过 handle(指向指针的指针) 来引用, 从而方便垃圾回收, 分为以下两种

  • v8::Local: 局部的, 本地句柄, 提供对栈(stack)分配的 JS 对象的引用
    • HandleScope 是 Local handle 的集合栈. 有了它就可以统一管理 Local handle, 比如统一释放内存
    • v8::MaybeLocal
      • 为了让用户注意空指针的情况出现的类型, 可以通过 ToLocalChecked() 或者 ToLocal() 转换为 v8::Local
      •  

MaybeLocalv8::String str = v8::String::NewFromUtf8(isolate, “str”); // 使用 ToLocalChecked() v8::Localv8::String src = str.ToLocalChecked(); // 使用 ToLocal() v8::Localv8::String src; if (!str.ToLocal(&src)) { return; }


      - [这里](https://stackoverflow.com/questions/47114090/what-does-an-empty-maybelocal-mean/47144507#47144507)有一个 V8 开发者 jmrk 的解释, idom 的[这篇文章](https://idom.me/articles/847.html)也很好的解释了
- `v8::Persistent`: 全局的, 持久化句柄, 提供对堆(heap)分配的 JS 对象的引用, 比如 Chrome 里的 DOM 节点
### context/数据/模板

- `v8::context`: 相当于全局变量, 浏览器中就是 window 对象啦
   - 使用 context 可以将互相分离的 JS 脚本放在同一个 V8 的实例中运行且互不干涉, 比如浏览器中每个窗口和 iFrame 都有自己独立的 JS 环境
- `v8::Data`: 数据
   - `v8::String`
   - `v8::Integer`
- `v8::Template`: 模板
   - `v8::FunctionTemplate`: 函数模板
   - `v8::ObjectTemplate`: 对象模板
```cpp
/* 创建一个全局变量, 并在全局变量上加上 Print 方法 */
void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
  // do something
}
v8::Local<String> fn_name = v8::String::NewFromUtf8Literal(isolate, "print")
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
// 将 print 方法绑定到 全局对象 global
global->Set(fn_name, v8::FunctionTemplate::New(isolate, Print));
// 根据全局对象 global 创建上下文
v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
// 这样就可以在 js 里执行 print() 了

// 主动执行 print 函数, 参数为 "hello"
v8::Local<Value> callback = context->Global()->Get(context, fn_name).ToLocalChecked();
v8::Local<Value> args[] = {v8::String::NewFromUtf8(isolate, "hello").ToLocalChecked()};
v8::Local<Function>::Cast(callback)->Call(context, v8::Undefined(isolate), 1, args);

node.js 的插件示例里也有类似的写法举例

Accessor(访问器)

在脚本访问特定对象属性时运行 通过 SetAccessor 方法设置

Interceptor(拦截器)

在脚本访问任何对象属性时运行 通过 SetHandler方法设置, 官方文档里说的 SetNamedPropertyHandler 方法貌似已经废弃了

基本步骤

  • 初始化 V8
  • 创建一个新的隔离区(Isolate), 并将这个隔离区置为当前可用
  • 创建一个栈分配的句柄范围(HandleScope)来存储句柄
  • 创建一个上下文(Context)
  • 在上下文内编译和运行 JS 脚本
  • 销毁隔离区并关掉 V8 进程

Misc

源码

全局: namespace i = v8::internal;

如何正确地使用v8嵌入到我们的C++应用中 by 豆米 V8 javascript engine代码阅读 by Sakura