function_template.h 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // 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.
  4. #ifndef GIN_FUNCTION_TEMPLATE_H_
  5. #define GIN_FUNCTION_TEMPLATE_H_
  6. #include <stddef.h>
  7. #include <utility>
  8. #include "base/callback.h"
  9. #include "base/check.h"
  10. #include "base/memory/raw_ptr.h"
  11. #include "base/strings/strcat.h"
  12. #include "gin/arguments.h"
  13. #include "gin/converter.h"
  14. #include "gin/gin_export.h"
  15. #include "v8/include/v8-external.h"
  16. #include "v8/include/v8-forward.h"
  17. #include "v8/include/v8-persistent-handle.h"
  18. #include "v8/include/v8-template.h"
  19. namespace gin {
  20. struct InvokerOptions {
  21. bool holder_is_first_argument = false;
  22. const char* holder_type = nullptr; // Null if unknown or not applicable.
  23. };
  24. namespace internal {
  25. template<typename T>
  26. struct CallbackParamTraits {
  27. typedef T LocalType;
  28. };
  29. template<typename T>
  30. struct CallbackParamTraits<const T&> {
  31. typedef T LocalType;
  32. };
  33. template<typename T>
  34. struct CallbackParamTraits<const T*> {
  35. typedef T* LocalType;
  36. };
  37. // CallbackHolder and CallbackHolderBase are used to pass a
  38. // base::RepeatingCallback from CreateFunctionTemplate through v8 (via
  39. // v8::FunctionTemplate) to DispatchToCallback, where it is invoked.
  40. // This simple base class is used so that we can share a single object template
  41. // among every CallbackHolder instance.
  42. class GIN_EXPORT CallbackHolderBase {
  43. public:
  44. CallbackHolderBase(const CallbackHolderBase&) = delete;
  45. CallbackHolderBase& operator=(const CallbackHolderBase&) = delete;
  46. v8::Local<v8::External> GetHandle(v8::Isolate* isolate);
  47. protected:
  48. explicit CallbackHolderBase(v8::Isolate* isolate);
  49. virtual ~CallbackHolderBase();
  50. private:
  51. static void FirstWeakCallback(
  52. const v8::WeakCallbackInfo<CallbackHolderBase>& data);
  53. static void SecondWeakCallback(
  54. const v8::WeakCallbackInfo<CallbackHolderBase>& data);
  55. v8::Global<v8::External> v8_ref_;
  56. };
  57. template<typename Sig>
  58. class CallbackHolder : public CallbackHolderBase {
  59. public:
  60. CallbackHolder(v8::Isolate* isolate,
  61. base::RepeatingCallback<Sig> callback,
  62. InvokerOptions invoker_options)
  63. : CallbackHolderBase(isolate),
  64. callback(std::move(callback)),
  65. invoker_options(std::move(invoker_options)) {}
  66. CallbackHolder(const CallbackHolder&) = delete;
  67. CallbackHolder& operator=(const CallbackHolder&) = delete;
  68. base::RepeatingCallback<Sig> callback;
  69. InvokerOptions invoker_options;
  70. private:
  71. ~CallbackHolder() override = default;
  72. };
  73. template <typename T>
  74. bool GetNextArgument(Arguments* args,
  75. const InvokerOptions& invoker_options,
  76. bool is_first,
  77. T* result) {
  78. if (is_first && invoker_options.holder_is_first_argument) {
  79. return args->GetHolder(result);
  80. } else {
  81. return args->GetNext(result);
  82. }
  83. }
  84. // For advanced use cases, we allow callers to request the unparsed Arguments
  85. // object and poke around in it directly.
  86. inline bool GetNextArgument(Arguments* args,
  87. const InvokerOptions& invoker_options,
  88. bool is_first,
  89. Arguments* result) {
  90. *result = *args;
  91. return true;
  92. }
  93. inline bool GetNextArgument(Arguments* args,
  94. const InvokerOptions& invoker_options,
  95. bool is_first,
  96. Arguments** result) {
  97. *result = args;
  98. return true;
  99. }
  100. // It's common for clients to just need the isolate, so we make that easy.
  101. inline bool GetNextArgument(Arguments* args,
  102. const InvokerOptions& invoker_options,
  103. bool is_first,
  104. v8::Isolate** result) {
  105. *result = args->isolate();
  106. return true;
  107. }
  108. // Throws an error indicating conversion failure.
  109. GIN_EXPORT void ThrowConversionError(Arguments* args,
  110. const InvokerOptions& invoker_options,
  111. size_t index);
  112. // Class template for extracting and storing single argument for callback
  113. // at position |index|.
  114. template <size_t index, typename ArgType>
  115. struct ArgumentHolder {
  116. using ArgLocalType = typename CallbackParamTraits<ArgType>::LocalType;
  117. ArgLocalType value;
  118. bool ok;
  119. ArgumentHolder(Arguments* args, const InvokerOptions& invoker_options)
  120. : ok(GetNextArgument(args, invoker_options, index == 0, &value)) {
  121. if (!ok)
  122. ThrowConversionError(args, invoker_options, index);
  123. }
  124. };
  125. // Class template for converting arguments from JavaScript to C++ and running
  126. // the callback with them.
  127. template <typename IndicesType, typename... ArgTypes>
  128. class Invoker;
  129. template <size_t... indices, typename... ArgTypes>
  130. class Invoker<std::index_sequence<indices...>, ArgTypes...>
  131. : public ArgumentHolder<indices, ArgTypes>... {
  132. public:
  133. // Invoker<> inherits from ArgumentHolder<> for each argument.
  134. // C++ has always been strict about the class initialization order,
  135. // so it is guaranteed ArgumentHolders will be initialized (and thus, will
  136. // extract arguments from Arguments) in the right order.
  137. Invoker(Arguments* args, const InvokerOptions& invoker_options)
  138. : ArgumentHolder<indices, ArgTypes>(args, invoker_options)...,
  139. args_(args) {}
  140. bool IsOK() {
  141. return And(ArgumentHolder<indices, ArgTypes>::ok...);
  142. }
  143. template <typename ReturnType>
  144. void DispatchToCallback(
  145. base::RepeatingCallback<ReturnType(ArgTypes...)> callback) {
  146. args_->Return(
  147. callback.Run(std::move(ArgumentHolder<indices, ArgTypes>::value)...));
  148. }
  149. // In C++, you can declare the function foo(void), but you can't pass a void
  150. // expression to foo. As a result, we must specialize the case of Callbacks
  151. // that have the void return type.
  152. void DispatchToCallback(base::RepeatingCallback<void(ArgTypes...)> callback) {
  153. callback.Run(std::move(ArgumentHolder<indices, ArgTypes>::value)...);
  154. }
  155. private:
  156. static bool And() { return true; }
  157. template <typename... T>
  158. static bool And(bool arg1, T... args) {
  159. return arg1 && And(args...);
  160. }
  161. raw_ptr<Arguments> args_;
  162. };
  163. // DispatchToCallback converts all the JavaScript arguments to C++ types and
  164. // invokes the base::RepeatingCallback.
  165. template <typename Sig>
  166. struct Dispatcher {};
  167. template <typename ReturnType, typename... ArgTypes>
  168. struct Dispatcher<ReturnType(ArgTypes...)> {
  169. static void DispatchToCallbackImpl(Arguments* args) {
  170. v8::Local<v8::External> v8_holder;
  171. CHECK(args->GetData(&v8_holder));
  172. CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>(
  173. v8_holder->Value());
  174. typedef CallbackHolder<ReturnType(ArgTypes...)> HolderT;
  175. HolderT* holder = static_cast<HolderT*>(holder_base);
  176. using Indices = std::index_sequence_for<ArgTypes...>;
  177. Invoker<Indices, ArgTypes...> invoker(args, holder->invoker_options);
  178. if (invoker.IsOK())
  179. invoker.DispatchToCallback(holder->callback);
  180. }
  181. static void DispatchToCallback(
  182. const v8::FunctionCallbackInfo<v8::Value>& info) {
  183. Arguments args(info);
  184. DispatchToCallbackImpl(&args);
  185. }
  186. static void DispatchToCallbackForProperty(
  187. v8::Local<v8::Name>,
  188. const v8::PropertyCallbackInfo<v8::Value>& info) {
  189. Arguments args(info);
  190. DispatchToCallbackImpl(&args);
  191. }
  192. };
  193. } // namespace internal
  194. // CreateFunctionTemplate creates a v8::FunctionTemplate that will create
  195. // JavaScript functions that execute a provided C++ function or
  196. // base::RepeatingCallback. JavaScript arguments are automatically converted via
  197. // gin::Converter, as is the return value of the C++ function, if any.
  198. // |invoker_options| contains additional parameters. If it contains a
  199. // holder_type, it will be used to provide a useful conversion error if the
  200. // holder is the first argument. If not provided, a generic invocation error
  201. // will be used.
  202. //
  203. // NOTE: V8 caches FunctionTemplates for a lifetime of a web page for its own
  204. // internal reasons, thus it is generally a good idea to cache the template
  205. // returned by this function. Otherwise, repeated method invocations from JS
  206. // will create substantial memory leaks. See http://crbug.com/463487.
  207. template <typename Sig>
  208. v8::Local<v8::FunctionTemplate> CreateFunctionTemplate(
  209. v8::Isolate* isolate,
  210. base::RepeatingCallback<Sig> callback,
  211. InvokerOptions invoker_options = {}) {
  212. typedef internal::CallbackHolder<Sig> HolderT;
  213. HolderT* holder =
  214. new HolderT(isolate, std::move(callback), std::move(invoker_options));
  215. v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
  216. isolate, &internal::Dispatcher<Sig>::DispatchToCallback,
  217. ConvertToV8<v8::Local<v8::External>>(isolate, holder->GetHandle(isolate)),
  218. v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kThrow);
  219. return tmpl;
  220. }
  221. // CreateDataPropertyCallback creates a v8::AccessorNameGetterCallback and
  222. // corresponding data value that will hold and execute the provided
  223. // base::RepeatingCallback, using automatic conversions similar to
  224. // |CreateFunctionTemplate|.
  225. //
  226. // It is expected that these will be passed to v8::Template::SetLazyDataProperty
  227. // or another similar function.
  228. template <typename Sig>
  229. std::pair<v8::AccessorNameGetterCallback, v8::Local<v8::Value>>
  230. CreateDataPropertyCallback(v8::Isolate* isolate,
  231. base::RepeatingCallback<Sig> callback,
  232. InvokerOptions invoker_options = {}) {
  233. typedef internal::CallbackHolder<Sig> HolderT;
  234. HolderT* holder =
  235. new HolderT(isolate, std::move(callback), std::move(invoker_options));
  236. return {&internal::Dispatcher<Sig>::DispatchToCallbackForProperty,
  237. ConvertToV8<v8::Local<v8::External>>(isolate,
  238. holder->GetHandle(isolate))};
  239. }
  240. } // namespace gin
  241. #endif // GIN_FUNCTION_TEMPLATE_H_