blob: 3f6a1ddfeadb1e7a47e257ae324c147f7fc6780d [file] [log] [blame]
kolczyk735c49b62014-10-24 13:06:041// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]37dacfa2013-11-26 03:31:044
[email protected]b520e132013-11-29 03:21:485#ifndef GIN_FUNCTION_TEMPLATE_H_
6#define GIN_FUNCTION_TEMPLATE_H_
7
avi90e658dd2015-12-21 07:16:198#include <stddef.h>
Jeremy Roman6a1242b2019-02-04 17:51:579#include <utility>
avi90e658dd2015-12-21 07:16:1910
[email protected]314cde12013-11-23 20:26:5111#include "base/callback.h"
Hans Wennborgc8b134b2020-06-19 21:15:3912#include "base/check.h"
avi90e658dd2015-12-21 07:16:1913#include "base/macros.h"
Devlin Cronine9db9842018-04-09 17:51:0514#include "base/strings/strcat.h"
[email protected]314cde12013-11-23 20:26:5115#include "gin/arguments.h"
16#include "gin/converter.h"
[email protected]48c21632013-12-12 21:32:3417#include "gin/gin_export.h"
[email protected]314cde12013-11-23 20:26:5118#include "v8/include/v8.h"
19
20namespace gin {
21
Devlin Cronine9db9842018-04-09 17:51:0522struct InvokerOptions {
23 bool holder_is_first_argument = false;
24 const char* holder_type = nullptr; // Null if unknown or not applicable.
[email protected]bf3dd3c2013-12-06 06:55:2525};
26
[email protected]37dacfa2013-11-26 03:31:0427namespace internal {
28
[email protected]314cde12013-11-23 20:26:5129template<typename T>
[email protected]bf3dd3c2013-12-06 06:55:2530struct CallbackParamTraits {
31 typedef T LocalType;
[email protected]314cde12013-11-23 20:26:5132};
33template<typename T>
[email protected]bf3dd3c2013-12-06 06:55:2534struct CallbackParamTraits<const T&> {
35 typedef T LocalType;
36};
37template<typename T>
38struct CallbackParamTraits<const T*> {
39 typedef T* LocalType;
[email protected]314cde12013-11-23 20:26:5140};
41
[email protected]37dacfa2013-11-26 03:31:0442// CallbackHolder and CallbackHolderBase are used to pass a base::Callback from
43// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to
[email protected]81f8b91b2013-11-26 21:02:5144// DispatchToCallback, where it is invoked.
[email protected]6fe56102013-12-08 07:10:5845
46// This simple base class is used so that we can share a single object template
47// among every CallbackHolder instance.
[email protected]bf0142902014-02-11 15:06:1248class GIN_EXPORT CallbackHolderBase {
[email protected]b4acaf82013-12-12 09:40:5049 public:
deepak.sfaaa1b62015-04-30 07:30:4850 v8::Local<v8::External> GetHandle(v8::Isolate* isolate);
[email protected]bf0142902014-02-11 15:06:1251
[email protected]314cde12013-11-23 20:26:5152 protected:
[email protected]bf0142902014-02-11 15:06:1253 explicit CallbackHolderBase(v8::Isolate* isolate);
54 virtual ~CallbackHolderBase();
55
56 private:
dcarney99ade9082015-04-22 09:55:4257 static void FirstWeakCallback(
58 const v8::WeakCallbackInfo<CallbackHolderBase>& data);
59 static void SecondWeakCallback(
60 const v8::WeakCallbackInfo<CallbackHolderBase>& data);
[email protected]bf0142902014-02-11 15:06:1261
dcarney99ade9082015-04-22 09:55:4262 v8::Global<v8::External> v8_ref_;
[email protected]bf0142902014-02-11 15:06:1263
64 DISALLOW_COPY_AND_ASSIGN(CallbackHolderBase);
[email protected]314cde12013-11-23 20:26:5165};
66
[email protected]314cde12013-11-23 20:26:5167template<typename Sig>
68class CallbackHolder : public CallbackHolderBase {
69 public:
[email protected]bf0142902014-02-11 15:06:1270 CallbackHolder(v8::Isolate* isolate,
tzikc21a0dc2017-11-14 08:23:4471 base::RepeatingCallback<Sig> callback,
Devlin Cronine9db9842018-04-09 17:51:0572 InvokerOptions invoker_options)
tzikc21a0dc2017-11-14 08:23:4473 : CallbackHolderBase(isolate),
74 callback(std::move(callback)),
Devlin Cronine9db9842018-04-09 17:51:0575 invoker_options(std::move(invoker_options)) {}
76
tzikc21a0dc2017-11-14 08:23:4477 base::RepeatingCallback<Sig> callback;
Devlin Cronine9db9842018-04-09 17:51:0578 InvokerOptions invoker_options;
79
[email protected]314cde12013-11-23 20:26:5180 private:
Takuto Ikuta171abf62018-04-27 16:29:2981 ~CallbackHolder() override {}
[email protected]bf0142902014-02-11 15:06:1282
83 DISALLOW_COPY_AND_ASSIGN(CallbackHolder);
[email protected]314cde12013-11-23 20:26:5184};
85
Devlin Cronine9db9842018-04-09 17:51:0586template <typename T>
87bool GetNextArgument(Arguments* args,
88 const InvokerOptions& invoker_options,
89 bool is_first,
[email protected]bf3dd3c2013-12-06 06:55:2590 T* result) {
Devlin Cronine9db9842018-04-09 17:51:0591 if (is_first && invoker_options.holder_is_first_argument) {
[email protected]bf3dd3c2013-12-06 06:55:2592 return args->GetHolder(result);
93 } else {
94 return args->GetNext(result);
95 }
96}
97
98// For advanced use cases, we allow callers to request the unparsed Arguments
99// object and poke around in it directly.
Devlin Cronine9db9842018-04-09 17:51:05100inline bool GetNextArgument(Arguments* args,
101 const InvokerOptions& invoker_options,
102 bool is_first,
[email protected]bf3dd3c2013-12-06 06:55:25103 Arguments* result) {
104 *result = *args;
105 return true;
106}
Devlin Cronine9db9842018-04-09 17:51:05107inline bool GetNextArgument(Arguments* args,
108 const InvokerOptions& invoker_options,
109 bool is_first,
[email protected]5b971af2014-01-06 22:20:54110 Arguments** result) {
111 *result = args;
112 return true;
113}
[email protected]bf3dd3c2013-12-06 06:55:25114
[email protected]2491f142013-12-21 17:54:37115// It's common for clients to just need the isolate, so we make that easy.
Devlin Cronine9db9842018-04-09 17:51:05116inline bool GetNextArgument(Arguments* args,
117 const InvokerOptions& invoker_options,
118 bool is_first,
119 v8::Isolate** result) {
[email protected]2491f142013-12-21 17:54:37120 *result = args->isolate();
121 return true;
122}
123
Devlin Cronine9db9842018-04-09 17:51:05124// Throws an error indicating conversion failure.
125GIN_EXPORT void ThrowConversionError(Arguments* args,
126 const InvokerOptions& invoker_options,
127 size_t index);
128
kolczyk735c49b62014-10-24 13:06:04129// Class template for extracting and storing single argument for callback
130// at position |index|.
131template <size_t index, typename ArgType>
132struct ArgumentHolder {
133 using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
134
135 ArgLocalType value;
136 bool ok;
137
Devlin Cronine9db9842018-04-09 17:51:05138 ArgumentHolder(Arguments* args, const InvokerOptions& invoker_options)
139 : ok(GetNextArgument(args, invoker_options, index == 0, &value)) {
140 if (!ok)
141 ThrowConversionError(args, invoker_options, index);
kolczyk735c49b62014-10-24 13:06:04142 }
143};
144
145// Class template for converting arguments from JavaScript to C++ and running
146// the callback with them.
147template <typename IndicesType, typename... ArgTypes>
tzikc21a0dc2017-11-14 08:23:44148class Invoker;
kolczyk735c49b62014-10-24 13:06:04149
150template <size_t... indices, typename... ArgTypes>
tzikc21a0dc2017-11-14 08:23:44151class Invoker<std::index_sequence<indices...>, ArgTypes...>
kolczyk735c49b62014-10-24 13:06:04152 : public ArgumentHolder<indices, ArgTypes>... {
153 public:
154 // Invoker<> inherits from ArgumentHolder<> for each argument.
155 // C++ has always been strict about the class initialization order,
156 // so it is guaranteed ArgumentHolders will be initialized (and thus, will
157 // extract arguments from Arguments) in the right order.
Devlin Cronine9db9842018-04-09 17:51:05158 Invoker(Arguments* args, const InvokerOptions& invoker_options)
159 : ArgumentHolder<indices, ArgTypes>(args, invoker_options)...,
160 args_(args) {}
kolczyk735c49b62014-10-24 13:06:04161
162 bool IsOK() {
163 return And(ArgumentHolder<indices, ArgTypes>::ok...);
164 }
165
166 template <typename ReturnType>
tzikc21a0dc2017-11-14 08:23:44167 void DispatchToCallback(
168 base::RepeatingCallback<ReturnType(ArgTypes...)> callback) {
Jeremy Apthorp789ac3b2020-04-01 01:06:45169 args_->Return(
170 callback.Run(std::move(ArgumentHolder<indices, ArgTypes>::value)...));
kolczyk735c49b62014-10-24 13:06:04171 }
172
173 // In C++, you can declare the function foo(void), but you can't pass a void
174 // expression to foo. As a result, we must specialize the case of Callbacks
175 // that have the void return type.
tzikc21a0dc2017-11-14 08:23:44176 void DispatchToCallback(base::RepeatingCallback<void(ArgTypes...)> callback) {
Jeremy Apthorp789ac3b2020-04-01 01:06:45177 callback.Run(std::move(ArgumentHolder<indices, ArgTypes>::value)...);
kolczyk735c49b62014-10-24 13:06:04178 }
179
180 private:
181 static bool And() { return true; }
182 template <typename... T>
183 static bool And(bool arg1, T... args) {
184 return arg1 && And(args...);
185 }
186
187 Arguments* args_;
188};
[email protected]bf3dd3c2013-12-06 06:55:25189
[email protected]81f8b91b2013-11-26 21:02:51190// DispatchToCallback converts all the JavaScript arguments to C++ types and
191// invokes the base::Callback.
kolczyk735c49b62014-10-24 13:06:04192template <typename Sig>
193struct Dispatcher {};
[email protected]bf3dd3c2013-12-06 06:55:25194
kolczyk735c49b62014-10-24 13:06:04195template <typename ReturnType, typename... ArgTypes>
196struct Dispatcher<ReturnType(ArgTypes...)> {
Jeremy Roman6a1242b2019-02-04 17:51:57197 static void DispatchToCallbackImpl(Arguments* args) {
deepak.sfaaa1b62015-04-30 07:30:48198 v8::Local<v8::External> v8_holder;
Jeremy Roman6a1242b2019-02-04 17:51:57199 CHECK(args->GetData(&v8_holder));
[email protected]bf0142902014-02-11 15:06:12200 CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>(
201 v8_holder->Value());
[email protected]314cde12013-11-23 20:26:51202
kolczyk735c49b62014-10-24 13:06:04203 typedef CallbackHolder<ReturnType(ArgTypes...)> HolderT;
[email protected]bf3dd3c2013-12-06 06:55:25204 HolderT* holder = static_cast<HolderT*>(holder_base);
[email protected]37dacfa2013-11-26 03:31:04205
tzikc21a0dc2017-11-14 08:23:44206 using Indices = std::index_sequence_for<ArgTypes...>;
Jeremy Roman6a1242b2019-02-04 17:51:57207 Invoker<Indices, ArgTypes...> invoker(args, holder->invoker_options);
kolczyk735c49b62014-10-24 13:06:04208 if (invoker.IsOK())
209 invoker.DispatchToCallback(holder->callback);
[email protected]d73341d12013-12-21 00:48:46210 }
Jeremy Roman6a1242b2019-02-04 17:51:57211
212 static void DispatchToCallback(
213 const v8::FunctionCallbackInfo<v8::Value>& info) {
214 Arguments args(info);
215 DispatchToCallbackImpl(&args);
216 }
217
218 static void DispatchToCallbackForProperty(
219 v8::Local<v8::Name>,
220 const v8::PropertyCallbackInfo<v8::Value>& info) {
221 Arguments args(info);
222 DispatchToCallbackImpl(&args);
223 }
[email protected]d73341d12013-12-21 00:48:46224};
225
[email protected]81f8b91b2013-11-26 21:02:51226} // namespace internal
227
[email protected]bf3dd3c2013-12-06 06:55:25228// CreateFunctionTemplate creates a v8::FunctionTemplate that will create
229// JavaScript functions that execute a provided C++ function or base::Callback.
230// JavaScript arguments are automatically converted via gin::Converter, as is
Devlin Cronine9db9842018-04-09 17:51:05231// the return value of the C++ function, if any. |invoker_options| contains
232// additional parameters. If it contains a holder_type, it will be used to
233// provide a useful conversion error if the holder is the first argument. If not
234// provided, a generic invocation error will be used.
mnaganov098ba6f2015-03-03 16:34:48235//
236// NOTE: V8 caches FunctionTemplates for a lifetime of a web page for its own
237// internal reasons, thus it is generally a good idea to cache the template
238// returned by this function. Otherwise, repeated method invocations from JS
239// will create substantial memory leaks. See http://crbug.com/463487.
tzikc21a0dc2017-11-14 08:23:44240template <typename Sig>
[email protected]81f8b91b2013-11-26 21:02:51241v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
tzikc21a0dc2017-11-14 08:23:44242 v8::Isolate* isolate,
243 base::RepeatingCallback<Sig> callback,
Devlin Cronine9db9842018-04-09 17:51:05244 InvokerOptions invoker_options = {}) {
[email protected]bf3dd3c2013-12-06 06:55:25245 typedef internal::CallbackHolder<Sig> HolderT;
Devlin Cronine9db9842018-04-09 17:51:05246 HolderT* holder =
247 new HolderT(isolate, std::move(callback), std::move(invoker_options));
[email protected]bf0142902014-02-11 15:06:12248
jochen596fd5e2016-07-06 12:29:50249 v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
250 isolate, &internal::Dispatcher<Sig>::DispatchToCallback,
251 ConvertToV8<v8::Local<v8::External>>(isolate,
252 holder->GetHandle(isolate)));
253 tmpl->RemovePrototype();
254 return tmpl;
[email protected]7618ebbb2013-11-27 03:38:26255}
256
Jeremy Roman6a1242b2019-02-04 17:51:57257// CreateDataPropertyCallback creates a v8::AccessorNameGetterCallback and
258// corresponding data value that will hold and execute the provided
259// base::RepeatingCallback, using automatic conversions similar to
260// |CreateFunctionTemplate|.
261//
262// It is expected that these will be passed to v8::Template::SetLazyDataProperty
263// or another similar function.
264template <typename Sig>
265std::pair<v8::AccessorNameGetterCallback, v8::Local<v8::Value>>
266CreateDataPropertyCallback(v8::Isolate* isolate,
267 base::RepeatingCallback<Sig> callback,
268 InvokerOptions invoker_options = {}) {
269 typedef internal::CallbackHolder<Sig> HolderT;
270 HolderT* holder =
271 new HolderT(isolate, std::move(callback), std::move(invoker_options));
272 return {&internal::Dispatcher<Sig>::DispatchToCallbackForProperty,
273 ConvertToV8<v8::Local<v8::External>>(isolate,
274 holder->GetHandle(isolate))};
275}
276
[email protected]314cde12013-11-23 20:26:51277} // namespace gin
[email protected]b520e132013-11-29 03:21:48278
279#endif // GIN_FUNCTION_TEMPLATE_H_