V8 Embedding - 函数传参、返回、异常处理
发表于 2018-08-06
作者: 灼灼团队
本文字数: 3526
阅读时长 ≈ 11.8 分钟

# 目标

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

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

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

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

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

1
2
3
4
5
6

# 代码 & 说明

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

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
135

# 编译

v8 编译相关请查看 {% post_link v8/compile-v8 Javascript Engine V8 Embedding - 编译 %}

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`
1

# 说明

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

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

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

# 相关链接

  • Getting started with embedding V8: https://v8.dev/docs/embed#advanced-guide
  • Node.js C++ Addons: https://nodejs.org/dist/latest-v10.x/docs/api/addons.html
联系我们
联系电话:17681177133
联系邮箱:admin@zhuo-zhuo.com
公司地址:合肥市高新区习友路2666号 (习友路和石莲南路交叉口西北角)二期304室
官网
博客
皖ICP备20009670号-2
合肥灼灼信息技术有限公司 | Copyright © 2020-present zhuo-zhuo.com