V8 Embedding - 回调函数
发表于 2018-08-07
作者: 灼灼团队
本文字数: 3027
阅读时长 ≈ 10.1 分钟

# 目标

上一篇写处理函数参数,漏掉了 callback 函数做为参数的情况,这里给个简单的示例。

用 C++ 写一个简单 RunFunc() v8 函数传给 js 环境,使以下代码可运行:

// app.js
function fn (msg) {
  console.log(msg)
}

RunFunc(fn)
1
2
3
4
5
6

# 代码 & 说明

// callback.cc
// callback.cc
#include <cstring>
#include <fstream>
#include <iostream>
#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;

// run js callback
void RunCallback(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  // 调用回调函数需要 context 对象
  Local<Context> context = isolate->GetCurrentContext();
  Local<Function> cb = Local<Function>::Cast(args[0]);
  // 准备回调函数的参数
  const unsigned argc = 1;
  Local<Value> argv[argc] = {String::NewFromUtf8(isolate, "hello world")};

  // 调用
  cb->Call(context, Null(isolate), argc, argv).ToLocalChecked();
}

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

    Local<FunctionTemplate> cb_fn_tpl = FunctionTemplate::New(isolate, RunCallback);

    // 初始化 `global_template`
    Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
    global_template->Set(String::NewFromUtf8(isolate, "console"), console_tpl);
    global_template->Set(String::NewFromUtf8(isolate, "RunFunc"), cb_fn_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

# 编译

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

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

# 执行

./inode app.js   # output: `hello world`
1

# 说明

代码已经比较清晰了,跟上一篇函数传参处理的内容是一致的,还配有简单的注释说明。

下篇,我们将探索如何封装 C++ 类,然后就可以无障碍的编写 Node.js C++ Addon 或 阅读 Node.js 源码了。

# 相关链接

  • 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