Js Engine V8 Embedding - C++ 函数暴露给 Javascript 环境

目标

上一篇我们把 C++ 环境中的对象暴露到 Javascript 环境中,这篇我们要将 C++ 环境中的 函数 暴露到 Javascript 环境中,写一个简单 console.log 函数传给 js 环境,使以下代码可运行:

1
2
// app.js
console.log('Hi, 老根!')

代码 & 说明

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// expose-func.cc

#include <iostream>
#include <cstring>
#include <fstream>
#include <sstream>
#include <string>

#include "include/libplatform/libplatform.h"
#include "include/v8.h"

using namespace v8;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;
using std::stringstream;
using std::strcpy;

// 定义一个函数,用来读取 js 代码
char* ReadSourceCodeFile(const char* path) {
ifstream f(path);
stringstream buffer;
buffer << f.rdbuf();
const string str = buffer.str();
char* cstr = new char[str.length() + 1];
strcpy(cstr, str.c_str());
return cstr;
}

// 这个函数会被暴露给 js 执行环境
void ConsoleLog(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<String> str = Local<String>::Cast(args[0]);
String::Utf8Value utf8(isolate, str);
cout << *utf8 << endl;
args.GetReturnValue().SetUndefined();
}

int main(int argc, char* argv[]) {
// 初始化 V8
V8::InitializeICUDefaultLocation(argv[0]);
V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<Platform> platform = platform::NewDefaultPlatform();
V8::InitializePlatform(platform.get());
V8::Initialize();

// 创建 Isolate 实例
Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
ArrayBuffer::Allocator::NewDefaultAllocator();
Isolate* isolate = Isolate::New(create_params);
{
Isolate::Scope isolate_scope(isolate);

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

// 创建一个 v8 对象模板 `console_tpl`
Local<ObjectTemplate> console_tpl = ObjectTemplate::New(isolate);

// 将 `ConsoleLog` 函数设置为 `console_tpl` 的 `log` 方法
console_tpl->Set(String::NewFromUtf8(isolate, "log"), FunctionTemplate::New(isolate, ConsoleLog));

// 创建 v8 对象模板 `global_template`
// 将上面的 `console_tpl` 设为其 `console` 属性
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
global_template->Set(String::NewFromUtf8(isolate, "console"), console_tpl);

// 创建 v8 Context,将 `global_template` 做为它的 global 对象模板
Local<Context> context = Context::New(isolate, NULL, global_template);

// 进入 or 使用 context
Context::Scope context_scope(context);

{

// js 文件通过命令行第一个参数传递进来,读取 js 文件代码
const char* js_code = ReadSourceCodeFile(argv[1]);
Local<String> source = String::NewFromUtf8(isolate, js_code);

// 编译
Local<Script> script = Script::Compile(context, source).ToLocalChecked();

// 执行
Local<Value> result = script->Run(context).ToLocalChecked();

delete js_code;
}
}

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

编译

v8 编译相关请查看 Javascript Engine V8 Embedding - 编译

1
g++ -I. -Iinclude expose-func.cc -lv8_monolith -Lout.gn/x64.release.sample/obj/ -pthread -std=c++0x -o inode

执行

1
./inode app.js   # output: `Hi, 老根!`

说明

和上一篇同理,将函数暴露给 js 和 将变量暴露给 js 是类似的。都是将 C++ 对象绑定到 Context Global 对象上,通过这种方式,我们就可以扩展 js 的能力了。

相关链接