Javascript Engine V8 Embedding - Hello World 讲解

通过一个 Hello World 示例来介绍 V8 的几个重要概念。

这个示例代码是基于 V8 (7.1) 的,要编译这个示例请查看 Javascript Engine V8 Embedding - 编译

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
int main(int argc, char* argv[]) {
// 初始化 V8
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();

// 创建一个 Isolate 实例, 它代表一个 JS VM 实例
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();

v8::Isolate* isolate = v8::Isolate::New(create_params);

{
// 使用 isolate
v8::Isolate::Scope isolate_scope(isolate);

// 创建一个 HandleScope,用于管理 Handle 的生命周期
v8::HandleScope handle_scope(isolate);

// 创建 Context,它是 javascript 代码执行的环境上下文对象
v8::Local<v8::Context> context = v8::Context::New(isolate);

// 进入 or 使用 context
v8::Context::Scope context_scope(context);
{
// 定义 JS 代码字符串
v8::Local<v8::String> source =
v8::String::NewFromUtf8(isolate, "'Hello' + ', World!'",
v8::NewStringType::kNormal).ToLocalChecked();
// 编译 JS 代码
v8::Local<v8::Script> script =
v8::Script::Compile(context, source).ToLocalChecked();

// 运行 JS 代码,并获取结果
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();

// 将运行结果转换为 UTF8 字符串,并打印
v8::String::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
}

// 释放 V8 相关资源
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete create_params.array_buffer_allocator;
return 0;
}

V8 重要概念

Isolate

一个 Isolate 就是一个 VM 实例,它有自己独立的 Heap。

Handle

一个 Handle 就是对一个 V8 object 的引用, V8 objects 都是分配在 Heap 中的,所有 V8 objects 都需要通过一个Handle 引用来访问,才能保证 V8 garbage collector 能工作。

Handle 又分 Local(局部) 和 Persistent(全局)两种。

Local Handle 的生命周期 使用 HandleScope 来管理,而 Persistent 是全局的 Handle, 需要通过 Persistent::New()Persistent::Release() 来创建和释放。

Handle Scope

一个 Handle Scope 可以理解为用来批量管理 Handle 的容器,这样我们就不用一个个的去释放 Handle,只要把它们的 Handle Scope 释放掉就可以了。

Handle Scope 相当于提供了 Handle Stack 机制来管理 Handles,当一个 Handle Scope 本身被释放的时候,会从 Handle Stack 上弹出所有基于它的 Local Handles

注意:这个 Handle Stack 并不是 C++ 调用栈Handle Scope 是在 C++ 调用栈 中的分配的,Handle Scope 必须创建在栈上,不能用 new 创建!

Context

Context 是 javascript 代码的执行环境;

想像一下,你有多个不相关的 js 代码要运行,这些代码运行的过程中可能会修改 V8 全局的一些状态,就需要给他们指定不同的 context 才能相互独立的隔离运行。

另外,我们可以为一个 context 设置预定义的全局属性,这个属性可以是 C++ 写的对象或方法,这样在 js 环境中,就能够调用 C++ 对象了,相当于扩充了 js 的能力。

比如,Node.js 中的 process 对象,就是一个预定义到 context 的对象,所以在 Node.js 中可以直接使用。同样的,浏览器中的 window document 等全局对象,也是预定义到 context 上的。

参考链接

Getting started with embedding V8: https://v8.dev/docs/embed#advanced-guide