/
callback.h
151 lines (133 loc) · 5.61 KB
/
callback.h
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright (c) 2019 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_COMMON_GIN_HELPER_CALLBACK_H_
#define SHELL_COMMON_GIN_HELPER_CALLBACK_H_
#include <utility>
#include <vector>
#include "base/bind.h"
#include "gin/dictionary.h"
#include "shell/common/gin_converters/std_converter.h"
#include "shell/common/gin_helper/function_template.h"
#include "shell/common/gin_helper/locker.h"
#include "shell/common/gin_helper/microtasks_scope.h"
// Implements safe convertions between JS functions and base::Callback.
namespace gin_helper {
template <typename T>
class RefCountedGlobal;
// Manages the V8 function with RAII.
class SafeV8Function {
public:
SafeV8Function(v8::Isolate* isolate, v8::Local<v8::Value> value);
SafeV8Function(const SafeV8Function& other);
~SafeV8Function();
bool IsAlive() const;
v8::Local<v8::Function> NewHandle(v8::Isolate* isolate) const;
private:
scoped_refptr<RefCountedGlobal<v8::Function>> v8_function_;
};
// Helper to invoke a V8 function with C++ parameters.
template <typename Sig>
struct V8FunctionInvoker {};
template <typename... ArgTypes>
struct V8FunctionInvoker<v8::Local<v8::Value>(ArgTypes...)> {
static v8::Local<v8::Value> Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
gin_helper::Locker locker(isolate);
v8::EscapableHandleScope handle_scope(isolate);
if (!function.IsAlive())
return v8::Null(isolate);
gin_helper::MicrotasksScope microtasks_scope(isolate, true);
v8::Local<v8::Function> holder = function.NewHandle(isolate);
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
std::vector<v8::Local<v8::Value>> args{
gin::ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
v8::MaybeLocal<v8::Value> ret = holder->Call(
context, holder, args.size(), args.empty() ? nullptr : &args.front());
if (ret.IsEmpty())
return v8::Undefined(isolate);
else
return handle_scope.Escape(ret.ToLocalChecked());
}
};
template <typename... ArgTypes>
struct V8FunctionInvoker<void(ArgTypes...)> {
static void Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
gin_helper::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
if (!function.IsAlive())
return;
gin_helper::MicrotasksScope microtasks_scope(isolate, true);
v8::Local<v8::Function> holder = function.NewHandle(isolate);
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
std::vector<v8::Local<v8::Value>> args{
gin::ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
holder
->Call(context, holder, args.size(),
args.empty() ? nullptr : &args.front())
.IsEmpty();
}
};
template <typename ReturnType, typename... ArgTypes>
struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
static ReturnType Go(v8::Isolate* isolate,
const SafeV8Function& function,
ArgTypes... raw) {
gin_helper::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
ReturnType ret = ReturnType();
if (!function.IsAlive())
return ret;
gin_helper::MicrotasksScope microtasks_scope(isolate, true);
v8::Local<v8::Function> holder = function.NewHandle(isolate);
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
std::vector<v8::Local<v8::Value>> args{
gin::ConvertToV8(isolate, std::forward<ArgTypes>(raw))...};
v8::Local<v8::Value> result;
auto maybe_result = holder->Call(context, holder, args.size(),
args.empty() ? nullptr : &args.front());
if (maybe_result.ToLocal(&result))
gin::Converter<ReturnType>::FromV8(isolate, result, &ret);
return ret;
}
};
// Helper to pass a C++ funtion to JavaScript.
using Translater = base::Callback<void(gin::Arguments* args)>;
v8::Local<v8::Value> CreateFunctionFromTranslater(v8::Isolate* isolate,
const Translater& translater,
bool one_time);
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
v8::Local<v8::Value> arg1,
v8::Local<v8::Value> arg2);
// Calls callback with Arguments.
template <typename Sig>
struct NativeFunctionInvoker {};
template <typename ReturnType, typename... ArgTypes>
struct NativeFunctionInvoker<ReturnType(ArgTypes...)> {
static void Go(base::Callback<ReturnType(ArgTypes...)> val,
gin::Arguments* args) {
using Indices = typename IndicesGenerator<sizeof...(ArgTypes)>::type;
Invoker<Indices, ArgTypes...> invoker(args, 0);
if (invoker.IsOK())
invoker.DispatchToCallback(val);
}
};
// Convert a callback to V8 without the call number limitation, this can easily
// cause memory leaks so use it with caution.
template <typename Sig>
v8::Local<v8::Value> CallbackToV8Leaked(
v8::Isolate* isolate,
const base::RepeatingCallback<Sig>& val) {
Translater translater = base::Bind(&NativeFunctionInvoker<Sig>::Go, val);
return CreateFunctionFromTranslater(isolate, translater, false);
}
} // namespace gin_helper
#endif // SHELL_COMMON_GIN_HELPER_CALLBACK_H_