blob: d94acc86bee5e74255a591438994ca64f3842459 [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>
9
[email protected]314cde12013-11-23 20:26:5110#include "base/callback.h"
11#include "base/logging.h"
avi90e658dd2015-12-21 07:16:1912#include "base/macros.h"
Devlin Cronine9db9842018-04-09 17:51:0513#include "base/strings/strcat.h"
[email protected]314cde12013-11-23 20:26:5114#include "gin/arguments.h"
15#include "gin/converter.h"
[email protected]48c21632013-12-12 21:32:3416#include "gin/gin_export.h"
[email protected]314cde12013-11-23 20:26:5117#include "v8/include/v8.h"
18
19namespace gin {
20
Devlin Cronine9db9842018-04-09 17:51:0521struct InvokerOptions {
22 bool holder_is_first_argument = false;
23 const char* holder_type = nullptr; // Null if unknown or not applicable.
[email protected]bf3dd3c2013-12-06 06:55:2524};
25
[email protected]37dacfa2013-11-26 03:31:0426namespace internal {
27
[email protected]314cde12013-11-23 20:26:5128template<typename T>
[email protected]bf3dd3c2013-12-06 06:55:2529struct CallbackParamTraits {
30 typedef T LocalType;
[email protected]314cde12013-11-23 20:26:5131};
32template<typename T>
[email protected]bf3dd3c2013-12-06 06:55:2533struct CallbackParamTraits<const T&> {
34 typedef T LocalType;
35};
36template<typename T>
37struct CallbackParamTraits<const T*> {
38 typedef T* LocalType;
[email protected]314cde12013-11-23 20:26:5139};
40
[email protected]37dacfa2013-11-26 03:31:0441// CallbackHolder and CallbackHolderBase are used to pass a base::Callback from
42// CreateFunctionTemplate through v8 (via v8::FunctionTemplate) to
[email protected]81f8b91b2013-11-26 21:02:5143// DispatchToCallback, where it is invoked.
[email protected]6fe56102013-12-08 07:10:5844
45// This simple base class is used so that we can share a single object template
46// among every CallbackHolder instance.
[email protected]bf0142902014-02-11 15:06:1247class GIN_EXPORT CallbackHolderBase {
[email protected]b4acaf82013-12-12 09:40:5048 public:
deepak.sfaaa1b62015-04-30 07:30:4849 v8::Local<v8::External> GetHandle(v8::Isolate* isolate);
[email protected]bf0142902014-02-11 15:06:1250
[email protected]314cde12013-11-23 20:26:5151 protected:
[email protected]bf0142902014-02-11 15:06:1252 explicit CallbackHolderBase(v8::Isolate* isolate);
53 virtual ~CallbackHolderBase();
54
55 private:
dcarney99ade9082015-04-22 09:55:4256 static void FirstWeakCallback(
57 const v8::WeakCallbackInfo<CallbackHolderBase>& data);
58 static void SecondWeakCallback(
59 const v8::WeakCallbackInfo<CallbackHolderBase>& data);
[email protected]bf0142902014-02-11 15:06:1260
dcarney99ade9082015-04-22 09:55:4261 v8::Global<v8::External> v8_ref_;
[email protected]bf0142902014-02-11 15:06:1262
63 DISALLOW_COPY_AND_ASSIGN(CallbackHolderBase);
[email protected]314cde12013-11-23 20:26:5164};
65
[email protected]314cde12013-11-23 20:26:5166template<typename Sig>
67class CallbackHolder : public CallbackHolderBase {
68 public:
[email protected]bf0142902014-02-11 15:06:1269 CallbackHolder(v8::Isolate* isolate,
tzikc21a0dc2017-11-14 08:23:4470 base::RepeatingCallback<Sig> callback,
Devlin Cronine9db9842018-04-09 17:51:0571 InvokerOptions invoker_options)
tzikc21a0dc2017-11-14 08:23:4472 : CallbackHolderBase(isolate),
73 callback(std::move(callback)),
Devlin Cronine9db9842018-04-09 17:51:0574 invoker_options(std::move(invoker_options)) {}
75
tzikc21a0dc2017-11-14 08:23:4476 base::RepeatingCallback<Sig> callback;
Devlin Cronine9db9842018-04-09 17:51:0577 InvokerOptions invoker_options;
78
[email protected]314cde12013-11-23 20:26:5179 private:
[email protected]695af7f52013-12-11 19:41:0180 virtual ~CallbackHolder() {}
[email protected]bf0142902014-02-11 15:06:1281
82 DISALLOW_COPY_AND_ASSIGN(CallbackHolder);
[email protected]314cde12013-11-23 20:26:5183};
84
Devlin Cronine9db9842018-04-09 17:51:0585template <typename T>
86bool GetNextArgument(Arguments* args,
87 const InvokerOptions& invoker_options,
88 bool is_first,
[email protected]bf3dd3c2013-12-06 06:55:2589 T* result) {
Devlin Cronine9db9842018-04-09 17:51:0590 if (is_first && invoker_options.holder_is_first_argument) {
[email protected]bf3dd3c2013-12-06 06:55:2591 return args->GetHolder(result);
92 } else {
93 return args->GetNext(result);
94 }
95}
96
97// For advanced use cases, we allow callers to request the unparsed Arguments
98// object and poke around in it directly.
Devlin Cronine9db9842018-04-09 17:51:0599inline bool GetNextArgument(Arguments* args,
100 const InvokerOptions& invoker_options,
101 bool is_first,
[email protected]bf3dd3c2013-12-06 06:55:25102 Arguments* result) {
103 *result = *args;
104 return true;
105}
Devlin Cronine9db9842018-04-09 17:51:05106inline bool GetNextArgument(Arguments* args,
107 const InvokerOptions& invoker_options,
108 bool is_first,
[email protected]5b971af2014-01-06 22:20:54109 Arguments** result) {
110 *result = args;
111 return true;
112}
[email protected]bf3dd3c2013-12-06 06:55:25113
[email protected]2491f142013-12-21 17:54:37114// It's common for clients to just need the isolate, so we make that easy.
Devlin Cronine9db9842018-04-09 17:51:05115inline bool GetNextArgument(Arguments* args,
116 const InvokerOptions& invoker_options,
117 bool is_first,
118 v8::Isolate** result) {
[email protected]2491f142013-12-21 17:54:37119 *result = args->isolate();
120 return true;
121}
122
Devlin Cronine9db9842018-04-09 17:51:05123// Throws an error indicating conversion failure.
124GIN_EXPORT void ThrowConversionError(Arguments* args,
125 const InvokerOptions& invoker_options,
126 size_t index);
127
kolczyk735c49b62014-10-24 13:06:04128// Class template for extracting and storing single argument for callback
129// at position |index|.
130template <size_t index, typename ArgType>
131struct ArgumentHolder {
132 using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
133
134 ArgLocalType value;
135 bool ok;
136
Devlin Cronine9db9842018-04-09 17:51:05137 ArgumentHolder(Arguments* args, const InvokerOptions& invoker_options)
138 : ok(GetNextArgument(args, invoker_options, index == 0, &value)) {
139 if (!ok)
140 ThrowConversionError(args, invoker_options, index);
kolczyk735c49b62014-10-24 13:06:04141 }
142};
143
144// Class template for converting arguments from JavaScript to C++ and running
145// the callback with them.
146template <typename IndicesType, typename... ArgTypes>
tzikc21a0dc2017-11-14 08:23:44147class Invoker;
kolczyk735c49b62014-10-24 13:06:04148
149template <size_t... indices, typename... ArgTypes>
tzikc21a0dc2017-11-14 08:23:44150class Invoker<std::index_sequence<indices...>, ArgTypes...>
kolczyk735c49b62014-10-24 13:06:04151 : public ArgumentHolder<indices, ArgTypes>... {
152 public:
153 // Invoker<> inherits from ArgumentHolder<> for each argument.
154 // C++ has always been strict about the class initialization order,
155 // so it is guaranteed ArgumentHolders will be initialized (and thus, will
156 // extract arguments from Arguments) in the right order.
Devlin Cronine9db9842018-04-09 17:51:05157 Invoker(Arguments* args, const InvokerOptions& invoker_options)
158 : ArgumentHolder<indices, ArgTypes>(args, invoker_options)...,
159 args_(args) {}
kolczyk735c49b62014-10-24 13:06:04160
161 bool IsOK() {
162 return And(ArgumentHolder<indices, ArgTypes>::ok...);
163 }
164
165 template <typename ReturnType>
tzikc21a0dc2017-11-14 08:23:44166 void DispatchToCallback(
167 base::RepeatingCallback<ReturnType(ArgTypes...)> callback) {
kolczyk735c49b62014-10-24 13:06:04168 args_->Return(callback.Run(ArgumentHolder<indices, ArgTypes>::value...));
169 }
170
171 // In C++, you can declare the function foo(void), but you can't pass a void
172 // expression to foo. As a result, we must specialize the case of Callbacks
173 // that have the void return type.
tzikc21a0dc2017-11-14 08:23:44174 void DispatchToCallback(base::RepeatingCallback<void(ArgTypes...)> callback) {
kolczyk735c49b62014-10-24 13:06:04175 callback.Run(ArgumentHolder<indices, ArgTypes>::value...);
176 }
177
178 private:
179 static bool And() { return true; }
180 template <typename... T>
181 static bool And(bool arg1, T... args) {
182 return arg1 && And(args...);
183 }
184
185 Arguments* args_;
186};
[email protected]bf3dd3c2013-12-06 06:55:25187
[email protected]81f8b91b2013-11-26 21:02:51188// DispatchToCallback converts all the JavaScript arguments to C++ types and
189// invokes the base::Callback.
kolczyk735c49b62014-10-24 13:06:04190template <typename Sig>
191struct Dispatcher {};
[email protected]bf3dd3c2013-12-06 06:55:25192
kolczyk735c49b62014-10-24 13:06:04193template <typename ReturnType, typename... ArgTypes>
194struct Dispatcher<ReturnType(ArgTypes...)> {
[email protected]bf3dd3c2013-12-06 06:55:25195 static void DispatchToCallback(
196 const v8::FunctionCallbackInfo<v8::Value>& info) {
197 Arguments args(info);
deepak.sfaaa1b62015-04-30 07:30:48198 v8::Local<v8::External> v8_holder;
[email protected]bf0142902014-02-11 15:06:12199 CHECK(args.GetData(&v8_holder));
200 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...>;
Devlin Cronine9db9842018-04-09 17:51:05207 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 }
211};
212
[email protected]81f8b91b2013-11-26 21:02:51213} // namespace internal
214
[email protected]bf3dd3c2013-12-06 06:55:25215// CreateFunctionTemplate creates a v8::FunctionTemplate that will create
216// JavaScript functions that execute a provided C++ function or base::Callback.
217// JavaScript arguments are automatically converted via gin::Converter, as is
Devlin Cronine9db9842018-04-09 17:51:05218// the return value of the C++ function, if any. |invoker_options| contains
219// additional parameters. If it contains a holder_type, it will be used to
220// provide a useful conversion error if the holder is the first argument. If not
221// provided, a generic invocation error will be used.
mnaganov098ba6f2015-03-03 16:34:48222//
223// NOTE: V8 caches FunctionTemplates for a lifetime of a web page for its own
224// internal reasons, thus it is generally a good idea to cache the template
225// returned by this function. Otherwise, repeated method invocations from JS
226// will create substantial memory leaks. See http://crbug.com/463487.
tzikc21a0dc2017-11-14 08:23:44227template <typename Sig>
[email protected]81f8b91b2013-11-26 21:02:51228v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
tzikc21a0dc2017-11-14 08:23:44229 v8::Isolate* isolate,
230 base::RepeatingCallback<Sig> callback,
Devlin Cronine9db9842018-04-09 17:51:05231 InvokerOptions invoker_options = {}) {
[email protected]bf3dd3c2013-12-06 06:55:25232 typedef internal::CallbackHolder<Sig> HolderT;
Devlin Cronine9db9842018-04-09 17:51:05233 HolderT* holder =
234 new HolderT(isolate, std::move(callback), std::move(invoker_options));
[email protected]bf0142902014-02-11 15:06:12235
jochen596fd5e2016-07-06 12:29:50236 v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
237 isolate, &internal::Dispatcher<Sig>::DispatchToCallback,
238 ConvertToV8<v8::Local<v8::External>>(isolate,
239 holder->GetHandle(isolate)));
240 tmpl->RemovePrototype();
241 return tmpl;
[email protected]7618ebbb2013-11-27 03:38:26242}
243
[email protected]314cde12013-11-23 20:26:51244} // namespace gin
[email protected]b520e132013-11-29 03:21:48245
246#endif // GIN_FUNCTION_TEMPLATE_H_