Js Engine V8 Embedding - V8 函数传参、返回、异常处理

目标

上一篇我们只是简单的把 C++ 环境中的函数暴露到 Javascript 环境中调用,这篇我们更进一步,学会操作函数参数、返回值以及 V8 异常处理。

写一个简单 Math.add() 函数传给 js 环境,使以下代码可运行:

1
2
3
4
5
// app.js
const a = 1023
const b = 1
const sum = Math.add(a, b)
console.log(sum)

然后我们修改一下这个 app.js,给 Math.add() 传入非法参数,学习处理 V8 异常:

1
2
3
4
5
// app.js
const a = "1023"
const b = 1
const sum = Math.add(a, b)
console.log(sum)

代码 & 说明

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// expose-func-params.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::strcpy;
using std::string;
using std::stringstream;

// 两个实数相加
void Add(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();

// 参数检验
if (args.Length() < 2) {
// 抛出可在 js 环境中捕获的异常
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong number of arguments",
NewStringType::kNormal)
.ToLocalChecked()));
return;
}

// 检查参数类型
if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate, "Wrong arguments", NewStringType::kNormal)
.ToLocalChecked()));
return;
}

// 执行加法操作
double value = args[0].As<Number>()->Value() + args[1].As<Number>()->Value();
Local<Number> num = Number::New(isolate, value);

// 使用 `args` 来 return 计算结果
args.GetReturnValue().Set(num);
}

// 读取 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;
}

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);

// 创建对象模板 `console_tpl`
Local<ObjectTemplate> console_tpl = ObjectTemplate::New(isolate);
console_tpl->Set(String::NewFromUtf8(isolate, "log"),
FunctionTemplate::New(isolate, ConsoleLog));

// 创建对象模板 `math_tpl`
Local<ObjectTemplate> math_tpl = ObjectTemplate::New(isolate);
math_tpl->Set(String::NewFromUtf8(isolate, "add"),
FunctionTemplate::New(isolate, Add));

// 初始化 `global_template`
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
global_template->Set(String::NewFromUtf8(isolate, "console"), console_tpl);
global_template->Set(String::NewFromUtf8(isolate, "Math"), math_tpl);

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

Context::Scope context_scope(context);

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

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

// v8 异常捕获
TryCatch trycatch(isolate);
MaybeLocal<Value> result = script->Run(context);
// v8 异常处理
if (trycatch.HasCaught()) {
Local<Value> exception = trycatch.Exception();
String::Utf8Value exception_str(isolate, exception);
cout<< *exception_str<< endl;
}

delete js_code;
}
}

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-params.cc -lv8_monolith -Lout.gn/x64.release.sample/obj/ -pthread -std=c++0x -o inode

执行

1
./inode app.js   # output: `1024`

说明

代码已经比较清晰了,还配有简单的注释说明。

这节主要是展示 V8 函数参数的处理,抛出 js 异常,在 C++ 中处理 V8 抛出的异常。

到这篇为止,我们已经可以很自由的扩展 js 的能力了, 可以用 V8 做出很 cool 的东西了。

相关链接