v8-fast-api-calls.h 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. // Copyright 2020 the V8 project 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. /**
  5. * This file provides additional API on top of the default one for making
  6. * API calls, which come from embedder C++ functions. The functions are being
  7. * called directly from optimized code, doing all the necessary typechecks
  8. * in the compiler itself, instead of on the embedder side. Hence the "fast"
  9. * in the name. Example usage might look like:
  10. *
  11. * \code
  12. * void FastMethod(int param, bool another_param);
  13. *
  14. * v8::FunctionTemplate::New(isolate, SlowCallback, data,
  15. * signature, length, constructor_behavior
  16. * side_effect_type,
  17. * &v8::CFunction::Make(FastMethod));
  18. * \endcode
  19. *
  20. * By design, fast calls are limited by the following requirements, which
  21. * the embedder should enforce themselves:
  22. * - they should not allocate on the JS heap;
  23. * - they should not trigger JS execution.
  24. * To enforce them, the embedder could use the existing
  25. * v8::Isolate::DisallowJavascriptExecutionScope and a utility similar to
  26. * Blink's NoAllocationScope:
  27. * https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/heap/thread_state_scopes.h;l=16
  28. *
  29. * Due to these limitations, it's not directly possible to report errors by
  30. * throwing a JS exception or to otherwise do an allocation. There is an
  31. * alternative way of creating fast calls that supports falling back to the
  32. * slow call and then performing the necessary allocation. When one creates
  33. * the fast method by using CFunction::MakeWithFallbackSupport instead of
  34. * CFunction::Make, the fast callback gets as last parameter an output variable,
  35. * through which it can request falling back to the slow call. So one might
  36. * declare their method like:
  37. *
  38. * \code
  39. * void FastMethodWithFallback(int param, FastApiCallbackOptions& options);
  40. * \endcode
  41. *
  42. * If the callback wants to signal an error condition or to perform an
  43. * allocation, it must set options.fallback to true and do an early return from
  44. * the fast method. Then V8 checks the value of options.fallback and if it's
  45. * true, falls back to executing the SlowCallback, which is capable of reporting
  46. * the error (either by throwing a JS exception or logging to the console) or
  47. * doing the allocation. It's the embedder's responsibility to ensure that the
  48. * fast callback is idempotent up to the point where error and fallback
  49. * conditions are checked, because otherwise executing the slow callback might
  50. * produce visible side-effects twice.
  51. *
  52. * An example for custom embedder type support might employ a way to wrap/
  53. * unwrap various C++ types in JSObject instances, e.g:
  54. *
  55. * \code
  56. *
  57. * // Helper method with a check for field count.
  58. * template <typename T, int offset>
  59. * inline T* GetInternalField(v8::Local<v8::Object> wrapper) {
  60. * assert(offset < wrapper->InternalFieldCount());
  61. * return reinterpret_cast<T*>(
  62. * wrapper->GetAlignedPointerFromInternalField(offset));
  63. * }
  64. *
  65. * class CustomEmbedderType {
  66. * public:
  67. * // Returns the raw C object from a wrapper JS object.
  68. * static CustomEmbedderType* Unwrap(v8::Local<v8::Object> wrapper) {
  69. * return GetInternalField<CustomEmbedderType,
  70. * kV8EmbedderWrapperObjectIndex>(wrapper);
  71. * }
  72. * static void FastMethod(v8::Local<v8::Object> receiver_obj, int param) {
  73. * CustomEmbedderType* receiver = static_cast<CustomEmbedderType*>(
  74. * receiver_obj->GetAlignedPointerFromInternalField(
  75. * kV8EmbedderWrapperObjectIndex));
  76. *
  77. * // Type checks are already done by the optimized code.
  78. * // Then call some performance-critical method like:
  79. * // receiver->Method(param);
  80. * }
  81. *
  82. * static void SlowMethod(
  83. * const v8::FunctionCallbackInfo<v8::Value>& info) {
  84. * v8::Local<v8::Object> instance =
  85. * v8::Local<v8::Object>::Cast(info.Holder());
  86. * CustomEmbedderType* receiver = Unwrap(instance);
  87. * // TODO: Do type checks and extract {param}.
  88. * receiver->Method(param);
  89. * }
  90. * };
  91. *
  92. * // TODO(mslekova): Clean-up these constants
  93. * // The constants kV8EmbedderWrapperTypeIndex and
  94. * // kV8EmbedderWrapperObjectIndex describe the offsets for the type info
  95. * // struct and the native object, when expressed as internal field indices
  96. * // within a JSObject. The existance of this helper function assumes that
  97. * // all embedder objects have their JSObject-side type info at the same
  98. * // offset, but this is not a limitation of the API itself. For a detailed
  99. * // use case, see the third example.
  100. * static constexpr int kV8EmbedderWrapperTypeIndex = 0;
  101. * static constexpr int kV8EmbedderWrapperObjectIndex = 1;
  102. *
  103. * // The following setup function can be templatized based on
  104. * // the {embedder_object} argument.
  105. * void SetupCustomEmbedderObject(v8::Isolate* isolate,
  106. * v8::Local<v8::Context> context,
  107. * CustomEmbedderType* embedder_object) {
  108. * isolate->set_embedder_wrapper_type_index(
  109. * kV8EmbedderWrapperTypeIndex);
  110. * isolate->set_embedder_wrapper_object_index(
  111. * kV8EmbedderWrapperObjectIndex);
  112. *
  113. * v8::CFunction c_func =
  114. * MakeV8CFunction(CustomEmbedderType::FastMethod);
  115. *
  116. * Local<v8::FunctionTemplate> method_template =
  117. * v8::FunctionTemplate::New(
  118. * isolate, CustomEmbedderType::SlowMethod, v8::Local<v8::Value>(),
  119. * v8::Local<v8::Signature>(), 1, v8::ConstructorBehavior::kAllow,
  120. * v8::SideEffectType::kHasSideEffect, &c_func);
  121. *
  122. * v8::Local<v8::ObjectTemplate> object_template =
  123. * v8::ObjectTemplate::New(isolate);
  124. * object_template->SetInternalFieldCount(
  125. * kV8EmbedderWrapperObjectIndex + 1);
  126. * object_template->Set(isolate, "method", method_template);
  127. *
  128. * // Instantiate the wrapper JS object.
  129. * v8::Local<v8::Object> object =
  130. * object_template->NewInstance(context).ToLocalChecked();
  131. * object->SetAlignedPointerInInternalField(
  132. * kV8EmbedderWrapperObjectIndex,
  133. * reinterpret_cast<void*>(embedder_object));
  134. *
  135. * // TODO: Expose {object} where it's necessary.
  136. * }
  137. * \endcode
  138. *
  139. * For instance if {object} is exposed via a global "obj" variable,
  140. * one could write in JS:
  141. * function hot_func() {
  142. * obj.method(42);
  143. * }
  144. * and once {hot_func} gets optimized, CustomEmbedderType::FastMethod
  145. * will be called instead of the slow version, with the following arguments:
  146. * receiver := the {embedder_object} from above
  147. * param := 42
  148. *
  149. * Currently supported return types:
  150. * - void
  151. * - bool
  152. * - int32_t
  153. * - uint32_t
  154. * - float32_t
  155. * - float64_t
  156. * Currently supported argument types:
  157. * - pointer to an embedder type
  158. * - JavaScript array of primitive types
  159. * - bool
  160. * - int32_t
  161. * - uint32_t
  162. * - int64_t
  163. * - uint64_t
  164. * - float32_t
  165. * - float64_t
  166. *
  167. * The 64-bit integer types currently have the IDL (unsigned) long long
  168. * semantics: https://heycam.github.io/webidl/#abstract-opdef-converttoint
  169. * In the future we'll extend the API to also provide conversions from/to
  170. * BigInt to preserve full precision.
  171. * The floating point types currently have the IDL (unrestricted) semantics,
  172. * which is the only one used by WebGL. We plan to add support also for
  173. * restricted floats/doubles, similarly to the BigInt conversion policies.
  174. * We also differ from the specific NaN bit pattern that WebIDL prescribes
  175. * (https://heycam.github.io/webidl/#es-unrestricted-float) in that Blink
  176. * passes NaN values as-is, i.e. doesn't normalize them.
  177. *
  178. * To be supported types:
  179. * - TypedArrays and ArrayBuffers
  180. * - arrays of embedder types
  181. *
  182. *
  183. * The API offers a limited support for function overloads:
  184. *
  185. * \code
  186. * void FastMethod_2Args(int param, bool another_param);
  187. * void FastMethod_3Args(int param, bool another_param, int third_param);
  188. *
  189. * v8::CFunction fast_method_2args_c_func =
  190. * MakeV8CFunction(FastMethod_2Args);
  191. * v8::CFunction fast_method_3args_c_func =
  192. * MakeV8CFunction(FastMethod_3Args);
  193. * const v8::CFunction fast_method_overloads[] = {fast_method_2args_c_func,
  194. * fast_method_3args_c_func};
  195. * Local<v8::FunctionTemplate> method_template =
  196. * v8::FunctionTemplate::NewWithCFunctionOverloads(
  197. * isolate, SlowCallback, data, signature, length,
  198. * constructor_behavior, side_effect_type,
  199. * {fast_method_overloads, 2});
  200. * \endcode
  201. *
  202. * In this example a single FunctionTemplate is associated to multiple C++
  203. * functions. The overload resolution is currently only based on the number of
  204. * arguments passed in a call. For example, if this method_template is
  205. * registered with a wrapper JS object as described above, a call with two
  206. * arguments:
  207. * obj.method(42, true);
  208. * will result in a fast call to FastMethod_2Args, while a call with three or
  209. * more arguments:
  210. * obj.method(42, true, 11);
  211. * will result in a fast call to FastMethod_3Args. Instead a call with less than
  212. * two arguments, like:
  213. * obj.method(42);
  214. * would not result in a fast call but would fall back to executing the
  215. * associated SlowCallback.
  216. */
  217. #ifndef INCLUDE_V8_FAST_API_CALLS_H_
  218. #define INCLUDE_V8_FAST_API_CALLS_H_
  219. #include <stddef.h>
  220. #include <stdint.h>
  221. #include <tuple>
  222. #include <type_traits>
  223. #include "v8-internal.h" // NOLINT(build/include_directory)
  224. #include "v8-local-handle.h" // NOLINT(build/include_directory)
  225. #include "v8-typed-array.h" // NOLINT(build/include_directory)
  226. #include "v8-value.h" // NOLINT(build/include_directory)
  227. #include "v8config.h" // NOLINT(build/include_directory)
  228. namespace v8 {
  229. class Isolate;
  230. class CTypeInfo {
  231. public:
  232. enum class Type : uint8_t {
  233. kVoid,
  234. kBool,
  235. kInt32,
  236. kUint32,
  237. kInt64,
  238. kUint64,
  239. kFloat32,
  240. kFloat64,
  241. kV8Value,
  242. kApiObject, // This will be deprecated once all users have
  243. // migrated from v8::ApiObject to v8::Local<v8::Value>.
  244. };
  245. // kCallbackOptionsType is not part of the Type enum
  246. // because it is only used internally. Use value 255 that is larger
  247. // than any valid Type enum.
  248. static constexpr Type kCallbackOptionsType = Type(255);
  249. enum class SequenceType : uint8_t {
  250. kScalar,
  251. kIsSequence, // sequence<T>
  252. kIsTypedArray, // TypedArray of T or any ArrayBufferView if T
  253. // is void
  254. kIsArrayBuffer // ArrayBuffer
  255. };
  256. enum class Flags : uint8_t {
  257. kNone = 0,
  258. kAllowSharedBit = 1 << 0, // Must be an ArrayBuffer or TypedArray
  259. kEnforceRangeBit = 1 << 1, // T must be integral
  260. kClampBit = 1 << 2, // T must be integral
  261. kIsRestrictedBit = 1 << 3, // T must be float or double
  262. };
  263. explicit constexpr CTypeInfo(
  264. Type type, SequenceType sequence_type = SequenceType::kScalar,
  265. Flags flags = Flags::kNone)
  266. : type_(type), sequence_type_(sequence_type), flags_(flags) {}
  267. typedef uint32_t Identifier;
  268. explicit constexpr CTypeInfo(Identifier identifier)
  269. : CTypeInfo(static_cast<Type>(identifier >> 16),
  270. static_cast<SequenceType>((identifier >> 8) & 255),
  271. static_cast<Flags>(identifier & 255)) {}
  272. constexpr Identifier GetId() const {
  273. return static_cast<uint8_t>(type_) << 16 |
  274. static_cast<uint8_t>(sequence_type_) << 8 |
  275. static_cast<uint8_t>(flags_);
  276. }
  277. constexpr Type GetType() const { return type_; }
  278. constexpr SequenceType GetSequenceType() const { return sequence_type_; }
  279. constexpr Flags GetFlags() const { return flags_; }
  280. static constexpr bool IsIntegralType(Type type) {
  281. return type == Type::kInt32 || type == Type::kUint32 ||
  282. type == Type::kInt64 || type == Type::kUint64;
  283. }
  284. static constexpr bool IsFloatingPointType(Type type) {
  285. return type == Type::kFloat32 || type == Type::kFloat64;
  286. }
  287. static constexpr bool IsPrimitive(Type type) {
  288. return IsIntegralType(type) || IsFloatingPointType(type) ||
  289. type == Type::kBool;
  290. }
  291. private:
  292. Type type_;
  293. SequenceType sequence_type_;
  294. Flags flags_;
  295. };
  296. struct FastApiTypedArrayBase {
  297. public:
  298. // Returns the length in number of elements.
  299. size_t V8_EXPORT length() const { return length_; }
  300. // Checks whether the given index is within the bounds of the collection.
  301. void V8_EXPORT ValidateIndex(size_t index) const;
  302. protected:
  303. size_t length_ = 0;
  304. };
  305. template <typename T>
  306. struct FastApiTypedArray : public FastApiTypedArrayBase {
  307. public:
  308. V8_INLINE T get(size_t index) const {
  309. #ifdef DEBUG
  310. ValidateIndex(index);
  311. #endif // DEBUG
  312. T tmp;
  313. memcpy(&tmp, reinterpret_cast<T*>(data_) + index, sizeof(T));
  314. return tmp;
  315. }
  316. bool getStorageIfAligned(T** elements) const {
  317. if (reinterpret_cast<uintptr_t>(data_) % alignof(T) != 0) {
  318. return false;
  319. }
  320. *elements = reinterpret_cast<T*>(data_);
  321. return true;
  322. }
  323. private:
  324. // This pointer should include the typed array offset applied.
  325. // It's not guaranteed that it's aligned to sizeof(T), it's only
  326. // guaranteed that it's 4-byte aligned, so for 8-byte types we need to
  327. // provide a special implementation for reading from it, which hides
  328. // the possibly unaligned read in the `get` method.
  329. void* data_;
  330. };
  331. // Any TypedArray. It uses kTypedArrayBit with base type void
  332. // Overloaded args of ArrayBufferView and TypedArray are not supported
  333. // (for now) because the generic “any” ArrayBufferView doesn’t have its
  334. // own instance type. It could be supported if we specify that
  335. // TypedArray<T> always has precedence over the generic ArrayBufferView,
  336. // but this complicates overload resolution.
  337. struct FastApiArrayBufferView {
  338. void* data;
  339. size_t byte_length;
  340. };
  341. struct FastApiArrayBuffer {
  342. void* data;
  343. size_t byte_length;
  344. };
  345. class V8_EXPORT CFunctionInfo {
  346. public:
  347. // Construct a struct to hold a CFunction's type information.
  348. // |return_info| describes the function's return type.
  349. // |arg_info| is an array of |arg_count| CTypeInfos describing the
  350. // arguments. Only the last argument may be of the special type
  351. // CTypeInfo::kCallbackOptionsType.
  352. CFunctionInfo(const CTypeInfo& return_info, unsigned int arg_count,
  353. const CTypeInfo* arg_info);
  354. const CTypeInfo& ReturnInfo() const { return return_info_; }
  355. // The argument count, not including the v8::FastApiCallbackOptions
  356. // if present.
  357. unsigned int ArgumentCount() const {
  358. return HasOptions() ? arg_count_ - 1 : arg_count_;
  359. }
  360. // |index| must be less than ArgumentCount().
  361. // Note: if the last argument passed on construction of CFunctionInfo
  362. // has type CTypeInfo::kCallbackOptionsType, it is not included in
  363. // ArgumentCount().
  364. const CTypeInfo& ArgumentInfo(unsigned int index) const;
  365. bool HasOptions() const {
  366. // The options arg is always the last one.
  367. return arg_count_ > 0 && arg_info_[arg_count_ - 1].GetType() ==
  368. CTypeInfo::kCallbackOptionsType;
  369. }
  370. private:
  371. const CTypeInfo return_info_;
  372. const unsigned int arg_count_;
  373. const CTypeInfo* arg_info_;
  374. };
  375. class V8_EXPORT CFunction {
  376. public:
  377. constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
  378. const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
  379. const CTypeInfo& ArgumentInfo(unsigned int index) const {
  380. return type_info_->ArgumentInfo(index);
  381. }
  382. unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
  383. const void* GetAddress() const { return address_; }
  384. const CFunctionInfo* GetTypeInfo() const { return type_info_; }
  385. enum class OverloadResolution { kImpossible, kAtRuntime, kAtCompileTime };
  386. // Returns whether an overload between this and the given CFunction can
  387. // be resolved at runtime by the RTTI available for the arguments or at
  388. // compile time for functions with different number of arguments.
  389. OverloadResolution GetOverloadResolution(const CFunction* other) {
  390. // Runtime overload resolution can only deal with functions with the
  391. // same number of arguments. Functions with different arity are handled
  392. // by compile time overload resolution though.
  393. if (ArgumentCount() != other->ArgumentCount()) {
  394. return OverloadResolution::kAtCompileTime;
  395. }
  396. // The functions can only differ by a single argument position.
  397. int diff_index = -1;
  398. for (unsigned int i = 0; i < ArgumentCount(); ++i) {
  399. if (ArgumentInfo(i).GetSequenceType() !=
  400. other->ArgumentInfo(i).GetSequenceType()) {
  401. if (diff_index >= 0) {
  402. return OverloadResolution::kImpossible;
  403. }
  404. diff_index = i;
  405. // We only support overload resolution between sequence types.
  406. if (ArgumentInfo(i).GetSequenceType() ==
  407. CTypeInfo::SequenceType::kScalar ||
  408. other->ArgumentInfo(i).GetSequenceType() ==
  409. CTypeInfo::SequenceType::kScalar) {
  410. return OverloadResolution::kImpossible;
  411. }
  412. }
  413. }
  414. return OverloadResolution::kAtRuntime;
  415. }
  416. template <typename F>
  417. static CFunction Make(F* func) {
  418. return ArgUnwrap<F*>::Make(func);
  419. }
  420. template <typename F>
  421. V8_DEPRECATED("Use CFunctionBuilder instead.")
  422. static CFunction MakeWithFallbackSupport(F* func) {
  423. return ArgUnwrap<F*>::Make(func);
  424. }
  425. CFunction(const void* address, const CFunctionInfo* type_info);
  426. private:
  427. const void* address_;
  428. const CFunctionInfo* type_info_;
  429. template <typename F>
  430. class ArgUnwrap {
  431. static_assert(sizeof(F) != sizeof(F),
  432. "CFunction must be created from a function pointer.");
  433. };
  434. template <typename R, typename... Args>
  435. class ArgUnwrap<R (*)(Args...)> {
  436. public:
  437. static CFunction Make(R (*func)(Args...));
  438. };
  439. };
  440. struct V8_DEPRECATE_SOON("Use v8::Local<v8::Value> instead.") ApiObject {
  441. uintptr_t address;
  442. };
  443. /**
  444. * A struct which may be passed to a fast call callback, like so:
  445. * \code
  446. * void FastMethodWithOptions(int param, FastApiCallbackOptions& options);
  447. * \endcode
  448. */
  449. struct FastApiCallbackOptions {
  450. /**
  451. * Creates a new instance of FastApiCallbackOptions for testing purpose. The
  452. * returned instance may be filled with mock data.
  453. */
  454. static FastApiCallbackOptions CreateForTesting(Isolate* isolate) {
  455. return {false, {0}};
  456. }
  457. /**
  458. * If the callback wants to signal an error condition or to perform an
  459. * allocation, it must set options.fallback to true and do an early return
  460. * from the fast method. Then V8 checks the value of options.fallback and if
  461. * it's true, falls back to executing the SlowCallback, which is capable of
  462. * reporting the error (either by throwing a JS exception or logging to the
  463. * console) or doing the allocation. It's the embedder's responsibility to
  464. * ensure that the fast callback is idempotent up to the point where error and
  465. * fallback conditions are checked, because otherwise executing the slow
  466. * callback might produce visible side-effects twice.
  467. */
  468. bool fallback;
  469. /**
  470. * The `data` passed to the FunctionTemplate constructor, or `undefined`.
  471. * `data_ptr` allows for default constructing FastApiCallbackOptions.
  472. */
  473. union {
  474. uintptr_t data_ptr;
  475. v8::Value data;
  476. };
  477. };
  478. namespace internal {
  479. // Helper to count the number of occurances of `T` in `List`
  480. template <typename T, typename... List>
  481. struct count : std::integral_constant<int, 0> {};
  482. template <typename T, typename... Args>
  483. struct count<T, T, Args...>
  484. : std::integral_constant<std::size_t, 1 + count<T, Args...>::value> {};
  485. template <typename T, typename U, typename... Args>
  486. struct count<T, U, Args...> : count<T, Args...> {};
  487. template <typename RetBuilder, typename... ArgBuilders>
  488. class CFunctionInfoImpl : public CFunctionInfo {
  489. static constexpr int kOptionsArgCount =
  490. count<FastApiCallbackOptions&, ArgBuilders...>();
  491. static constexpr int kReceiverCount = 1;
  492. static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1,
  493. "Only one options parameter is supported.");
  494. static_assert(sizeof...(ArgBuilders) >= kOptionsArgCount + kReceiverCount,
  495. "The receiver or the options argument is missing.");
  496. public:
  497. constexpr CFunctionInfoImpl()
  498. : CFunctionInfo(RetBuilder::Build(), sizeof...(ArgBuilders),
  499. arg_info_storage_),
  500. arg_info_storage_{ArgBuilders::Build()...} {
  501. constexpr CTypeInfo::Type kReturnType = RetBuilder::Build().GetType();
  502. static_assert(kReturnType == CTypeInfo::Type::kVoid ||
  503. kReturnType == CTypeInfo::Type::kBool ||
  504. kReturnType == CTypeInfo::Type::kInt32 ||
  505. kReturnType == CTypeInfo::Type::kUint32 ||
  506. kReturnType == CTypeInfo::Type::kFloat32 ||
  507. kReturnType == CTypeInfo::Type::kFloat64,
  508. "64-bit int and api object values are not currently "
  509. "supported return types.");
  510. }
  511. private:
  512. const CTypeInfo arg_info_storage_[sizeof...(ArgBuilders)];
  513. };
  514. template <typename T>
  515. struct TypeInfoHelper {
  516. static_assert(sizeof(T) != sizeof(T), "This type is not supported");
  517. };
  518. #define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR(T, Enum) \
  519. template <> \
  520. struct TypeInfoHelper<T> { \
  521. static constexpr CTypeInfo::Flags Flags() { \
  522. return CTypeInfo::Flags::kNone; \
  523. } \
  524. \
  525. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \
  526. static constexpr CTypeInfo::SequenceType SequenceType() { \
  527. return CTypeInfo::SequenceType::kScalar; \
  528. } \
  529. };
  530. template <CTypeInfo::Type type>
  531. struct CTypeInfoTraits {};
  532. #define DEFINE_TYPE_INFO_TRAITS(CType, Enum) \
  533. template <> \
  534. struct CTypeInfoTraits<CTypeInfo::Type::Enum> { \
  535. using ctype = CType; \
  536. };
  537. #define PRIMITIVE_C_TYPES(V) \
  538. V(bool, kBool) \
  539. V(int32_t, kInt32) \
  540. V(uint32_t, kUint32) \
  541. V(int64_t, kInt64) \
  542. V(uint64_t, kUint64) \
  543. V(float, kFloat32) \
  544. V(double, kFloat64)
  545. // Same as above, but includes deprecated types for compatibility.
  546. #define ALL_C_TYPES(V) \
  547. PRIMITIVE_C_TYPES(V) \
  548. V(void, kVoid) \
  549. V(v8::Local<v8::Value>, kV8Value) \
  550. V(v8::Local<v8::Object>, kV8Value) \
  551. V(ApiObject, kApiObject)
  552. // ApiObject was a temporary solution to wrap the pointer to the v8::Value.
  553. // Please use v8::Local<v8::Value> in new code for the arguments and
  554. // v8::Local<v8::Object> for the receiver, as ApiObject will be deprecated.
  555. ALL_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR)
  556. PRIMITIVE_C_TYPES(DEFINE_TYPE_INFO_TRAITS)
  557. #undef PRIMITIVE_C_TYPES
  558. #undef ALL_C_TYPES
  559. #define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA(T, Enum) \
  560. template <> \
  561. struct TypeInfoHelper<const FastApiTypedArray<T>&> { \
  562. static constexpr CTypeInfo::Flags Flags() { \
  563. return CTypeInfo::Flags::kNone; \
  564. } \
  565. \
  566. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \
  567. static constexpr CTypeInfo::SequenceType SequenceType() { \
  568. return CTypeInfo::SequenceType::kIsTypedArray; \
  569. } \
  570. };
  571. #define TYPED_ARRAY_C_TYPES(V) \
  572. V(int32_t, kInt32) \
  573. V(uint32_t, kUint32) \
  574. V(int64_t, kInt64) \
  575. V(uint64_t, kUint64) \
  576. V(float, kFloat32) \
  577. V(double, kFloat64)
  578. TYPED_ARRAY_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA)
  579. #undef TYPED_ARRAY_C_TYPES
  580. template <>
  581. struct TypeInfoHelper<v8::Local<v8::Array>> {
  582. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  583. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kVoid; }
  584. static constexpr CTypeInfo::SequenceType SequenceType() {
  585. return CTypeInfo::SequenceType::kIsSequence;
  586. }
  587. };
  588. template <>
  589. struct TypeInfoHelper<v8::Local<v8::Uint32Array>> {
  590. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  591. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kUint32; }
  592. static constexpr CTypeInfo::SequenceType SequenceType() {
  593. return CTypeInfo::SequenceType::kIsTypedArray;
  594. }
  595. };
  596. template <>
  597. struct TypeInfoHelper<FastApiCallbackOptions&> {
  598. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  599. static constexpr CTypeInfo::Type Type() {
  600. return CTypeInfo::kCallbackOptionsType;
  601. }
  602. static constexpr CTypeInfo::SequenceType SequenceType() {
  603. return CTypeInfo::SequenceType::kScalar;
  604. }
  605. };
  606. #define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \
  607. static_assert(((COND) == 0) || (ASSERTION), MSG)
  608. template <typename T, CTypeInfo::Flags... Flags>
  609. class CTypeInfoBuilder {
  610. public:
  611. using BaseType = T;
  612. static constexpr CTypeInfo Build() {
  613. constexpr CTypeInfo::Flags kFlags =
  614. MergeFlags(TypeInfoHelper<T>::Flags(), Flags...);
  615. constexpr CTypeInfo::Type kType = TypeInfoHelper<T>::Type();
  616. constexpr CTypeInfo::SequenceType kSequenceType =
  617. TypeInfoHelper<T>::SequenceType();
  618. STATIC_ASSERT_IMPLIES(
  619. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kAllowSharedBit),
  620. (kSequenceType == CTypeInfo::SequenceType::kIsTypedArray ||
  621. kSequenceType == CTypeInfo::SequenceType::kIsArrayBuffer),
  622. "kAllowSharedBit is only allowed for TypedArrays and ArrayBuffers.");
  623. STATIC_ASSERT_IMPLIES(
  624. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kEnforceRangeBit),
  625. CTypeInfo::IsIntegralType(kType),
  626. "kEnforceRangeBit is only allowed for integral types.");
  627. STATIC_ASSERT_IMPLIES(
  628. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kClampBit),
  629. CTypeInfo::IsIntegralType(kType),
  630. "kClampBit is only allowed for integral types.");
  631. STATIC_ASSERT_IMPLIES(
  632. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kIsRestrictedBit),
  633. CTypeInfo::IsFloatingPointType(kType),
  634. "kIsRestrictedBit is only allowed for floating point types.");
  635. STATIC_ASSERT_IMPLIES(kSequenceType == CTypeInfo::SequenceType::kIsSequence,
  636. kType == CTypeInfo::Type::kVoid,
  637. "Sequences are only supported from void type.");
  638. STATIC_ASSERT_IMPLIES(
  639. kSequenceType == CTypeInfo::SequenceType::kIsTypedArray,
  640. CTypeInfo::IsPrimitive(kType) || kType == CTypeInfo::Type::kVoid,
  641. "TypedArrays are only supported from primitive types or void.");
  642. // Return the same type with the merged flags.
  643. return CTypeInfo(TypeInfoHelper<T>::Type(),
  644. TypeInfoHelper<T>::SequenceType(), kFlags);
  645. }
  646. private:
  647. template <typename... Rest>
  648. static constexpr CTypeInfo::Flags MergeFlags(CTypeInfo::Flags flags,
  649. Rest... rest) {
  650. return CTypeInfo::Flags(uint8_t(flags) | uint8_t(MergeFlags(rest...)));
  651. }
  652. static constexpr CTypeInfo::Flags MergeFlags() { return CTypeInfo::Flags(0); }
  653. };
  654. template <typename RetBuilder, typename... ArgBuilders>
  655. class CFunctionBuilderWithFunction {
  656. public:
  657. explicit constexpr CFunctionBuilderWithFunction(const void* fn) : fn_(fn) {}
  658. template <CTypeInfo::Flags... Flags>
  659. constexpr auto Ret() {
  660. return CFunctionBuilderWithFunction<
  661. CTypeInfoBuilder<typename RetBuilder::BaseType, Flags...>,
  662. ArgBuilders...>(fn_);
  663. }
  664. template <unsigned int N, CTypeInfo::Flags... Flags>
  665. constexpr auto Arg() {
  666. // Return a copy of the builder with the Nth arg builder merged with
  667. // template parameter pack Flags.
  668. return ArgImpl<N, Flags...>(
  669. std::make_index_sequence<sizeof...(ArgBuilders)>());
  670. }
  671. auto Build() {
  672. static CFunctionInfoImpl<RetBuilder, ArgBuilders...> instance;
  673. return CFunction(fn_, &instance);
  674. }
  675. private:
  676. template <bool Merge, unsigned int N, CTypeInfo::Flags... Flags>
  677. struct GetArgBuilder;
  678. // Returns the same ArgBuilder as the one at index N, including its flags.
  679. // Flags in the template parameter pack are ignored.
  680. template <unsigned int N, CTypeInfo::Flags... Flags>
  681. struct GetArgBuilder<false, N, Flags...> {
  682. using type =
  683. typename std::tuple_element<N, std::tuple<ArgBuilders...>>::type;
  684. };
  685. // Returns an ArgBuilder with the same base type as the one at index N,
  686. // but merges the flags with the flags in the template parameter pack.
  687. template <unsigned int N, CTypeInfo::Flags... Flags>
  688. struct GetArgBuilder<true, N, Flags...> {
  689. using type = CTypeInfoBuilder<
  690. typename std::tuple_element<N,
  691. std::tuple<ArgBuilders...>>::type::BaseType,
  692. std::tuple_element<N, std::tuple<ArgBuilders...>>::type::Build()
  693. .GetFlags(),
  694. Flags...>;
  695. };
  696. // Return a copy of the CFunctionBuilder, but merges the Flags on
  697. // ArgBuilder index N with the new Flags passed in the template parameter
  698. // pack.
  699. template <unsigned int N, CTypeInfo::Flags... Flags, size_t... I>
  700. constexpr auto ArgImpl(std::index_sequence<I...>) {
  701. return CFunctionBuilderWithFunction<
  702. RetBuilder, typename GetArgBuilder<N == I, I, Flags...>::type...>(fn_);
  703. }
  704. const void* fn_;
  705. };
  706. class CFunctionBuilder {
  707. public:
  708. constexpr CFunctionBuilder() {}
  709. template <typename R, typename... Args>
  710. constexpr auto Fn(R (*fn)(Args...)) {
  711. return CFunctionBuilderWithFunction<CTypeInfoBuilder<R>,
  712. CTypeInfoBuilder<Args>...>(
  713. reinterpret_cast<const void*>(fn));
  714. }
  715. };
  716. } // namespace internal
  717. // static
  718. template <typename R, typename... Args>
  719. CFunction CFunction::ArgUnwrap<R (*)(Args...)>::Make(R (*func)(Args...)) {
  720. return internal::CFunctionBuilder().Fn(func).Build();
  721. }
  722. using CFunctionBuilder = internal::CFunctionBuilder;
  723. static constexpr CTypeInfo kTypeInfoInt32 = CTypeInfo(CTypeInfo::Type::kInt32);
  724. static constexpr CTypeInfo kTypeInfoFloat64 =
  725. CTypeInfo(CTypeInfo::Type::kFloat64);
  726. /**
  727. * Copies the contents of this JavaScript array to a C++ buffer with
  728. * a given max_length. A CTypeInfo is passed as an argument,
  729. * instructing different rules for conversion (e.g. restricted float/double).
  730. * The element type T of the destination array must match the C type
  731. * corresponding to the CTypeInfo (specified by CTypeInfoTraits).
  732. * If the array length is larger than max_length or the array is of
  733. * unsupported type, the operation will fail, returning false. Generally, an
  734. * array which contains objects, undefined, null or anything not convertible
  735. * to the requested destination type, is considered unsupported. The operation
  736. * returns true on success. `type_info` will be used for conversions.
  737. */
  738. template <const CTypeInfo* type_info, typename T>
  739. V8_DEPRECATE_SOON(
  740. "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
  741. bool V8_EXPORT V8_WARN_UNUSED_RESULT
  742. TryCopyAndConvertArrayToCppBuffer(Local<Array> src, T* dst,
  743. uint32_t max_length);
  744. template <>
  745. V8_DEPRECATE_SOON(
  746. "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
  747. inline bool V8_WARN_UNUSED_RESULT
  748. TryCopyAndConvertArrayToCppBuffer<&kTypeInfoInt32, int32_t>(
  749. Local<Array> src, int32_t* dst, uint32_t max_length) {
  750. return false;
  751. }
  752. template <>
  753. V8_DEPRECATE_SOON(
  754. "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
  755. inline bool V8_WARN_UNUSED_RESULT
  756. TryCopyAndConvertArrayToCppBuffer<&kTypeInfoFloat64, double>(
  757. Local<Array> src, double* dst, uint32_t max_length) {
  758. return false;
  759. }
  760. template <CTypeInfo::Identifier type_info_id, typename T>
  761. bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer(
  762. Local<Array> src, T* dst, uint32_t max_length);
  763. template <>
  764. bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer<
  765. internal::CTypeInfoBuilder<int32_t>::Build().GetId(), int32_t>(
  766. Local<Array> src, int32_t* dst, uint32_t max_length);
  767. template <>
  768. bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer<
  769. internal::CTypeInfoBuilder<uint32_t>::Build().GetId(), uint32_t>(
  770. Local<Array> src, uint32_t* dst, uint32_t max_length);
  771. template <>
  772. bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer<
  773. internal::CTypeInfoBuilder<float>::Build().GetId(), float>(
  774. Local<Array> src, float* dst, uint32_t max_length);
  775. template <>
  776. bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer<
  777. internal::CTypeInfoBuilder<double>::Build().GetId(), double>(
  778. Local<Array> src, double* dst, uint32_t max_length);
  779. } // namespace v8
  780. #endif // INCLUDE_V8_FAST_API_CALLS_H_