v8-fast-api-calls-v16.h 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  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.h" // NOLINT(build/include_directory)
  224. #include "v8config.h" // NOLINT(build/include_directory)
  225. namespace v8 {
  226. class Isolate;
  227. class CTypeInfo {
  228. public:
  229. enum class Type : uint8_t {
  230. kVoid,
  231. kBool,
  232. kInt32,
  233. kUint32,
  234. kInt64,
  235. kUint64,
  236. kFloat32,
  237. kFloat64,
  238. kV8Value,
  239. kApiObject, // This will be deprecated once all users have
  240. // migrated from v8::ApiObject to v8::Local<v8::Value>.
  241. };
  242. // kCallbackOptionsType is not part of the Type enum
  243. // because it is only used internally. Use value 255 that is larger
  244. // than any valid Type enum.
  245. static constexpr Type kCallbackOptionsType = Type(255);
  246. enum class SequenceType : uint8_t {
  247. kScalar,
  248. kIsSequence, // sequence<T>
  249. kIsTypedArray, // TypedArray of T or any ArrayBufferView if T
  250. // is void
  251. kIsArrayBuffer // ArrayBuffer
  252. };
  253. enum class Flags : uint8_t {
  254. kNone = 0,
  255. kAllowSharedBit = 1 << 0, // Must be an ArrayBuffer or TypedArray
  256. kEnforceRangeBit = 1 << 1, // T must be integral
  257. kClampBit = 1 << 2, // T must be integral
  258. kIsRestrictedBit = 1 << 3, // T must be float or double
  259. };
  260. explicit constexpr CTypeInfo(
  261. Type type, SequenceType sequence_type = SequenceType::kScalar,
  262. Flags flags = Flags::kNone)
  263. : type_(type), sequence_type_(sequence_type), flags_(flags) {}
  264. constexpr Type GetType() const { return type_; }
  265. constexpr SequenceType GetSequenceType() const { return sequence_type_; }
  266. constexpr Flags GetFlags() const { return flags_; }
  267. static constexpr bool IsIntegralType(Type type) {
  268. return type == Type::kInt32 || type == Type::kUint32 ||
  269. type == Type::kInt64 || type == Type::kUint64;
  270. }
  271. static constexpr bool IsFloatingPointType(Type type) {
  272. return type == Type::kFloat32 || type == Type::kFloat64;
  273. }
  274. static constexpr bool IsPrimitive(Type type) {
  275. return IsIntegralType(type) || IsFloatingPointType(type) ||
  276. type == Type::kBool;
  277. }
  278. private:
  279. Type type_;
  280. SequenceType sequence_type_;
  281. Flags flags_;
  282. };
  283. template <typename T>
  284. struct FastApiTypedArray {
  285. T* data; // should include the typed array offset applied
  286. size_t length; // length in number of elements
  287. };
  288. // Any TypedArray. It uses kTypedArrayBit with base type void
  289. // Overloaded args of ArrayBufferView and TypedArray are not supported
  290. // (for now) because the generic “any” ArrayBufferView doesn’t have its
  291. // own instance type. It could be supported if we specify that
  292. // TypedArray<T> always has precedence over the generic ArrayBufferView,
  293. // but this complicates overload resolution.
  294. struct FastApiArrayBufferView {
  295. void* data;
  296. size_t byte_length;
  297. };
  298. struct FastApiArrayBuffer {
  299. void* data;
  300. size_t byte_length;
  301. };
  302. class V8_EXPORT CFunctionInfo {
  303. public:
  304. // Construct a struct to hold a CFunction's type information.
  305. // |return_info| describes the function's return type.
  306. // |arg_info| is an array of |arg_count| CTypeInfos describing the
  307. // arguments. Only the last argument may be of the special type
  308. // CTypeInfo::kCallbackOptionsType.
  309. CFunctionInfo(const CTypeInfo& return_info, unsigned int arg_count,
  310. const CTypeInfo* arg_info);
  311. const CTypeInfo& ReturnInfo() const { return return_info_; }
  312. // The argument count, not including the v8::FastApiCallbackOptions
  313. // if present.
  314. unsigned int ArgumentCount() const {
  315. return HasOptions() ? arg_count_ - 1 : arg_count_;
  316. }
  317. // |index| must be less than ArgumentCount().
  318. // Note: if the last argument passed on construction of CFunctionInfo
  319. // has type CTypeInfo::kCallbackOptionsType, it is not included in
  320. // ArgumentCount().
  321. const CTypeInfo& ArgumentInfo(unsigned int index) const;
  322. bool HasOptions() const {
  323. // The options arg is always the last one.
  324. return arg_count_ > 0 && arg_info_[arg_count_ - 1].GetType() ==
  325. CTypeInfo::kCallbackOptionsType;
  326. }
  327. private:
  328. const CTypeInfo return_info_;
  329. const unsigned int arg_count_;
  330. const CTypeInfo* arg_info_;
  331. };
  332. class V8_EXPORT CFunction {
  333. public:
  334. constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
  335. const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
  336. const CTypeInfo& ArgumentInfo(unsigned int index) const {
  337. return type_info_->ArgumentInfo(index);
  338. }
  339. unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
  340. const void* GetAddress() const { return address_; }
  341. const CFunctionInfo* GetTypeInfo() const { return type_info_; }
  342. enum class OverloadResolution { kImpossible, kAtRuntime, kAtCompileTime };
  343. // Returns whether an overload between this and the given CFunction can
  344. // be resolved at runtime by the RTTI available for the arguments or at
  345. // compile time for functions with different number of arguments.
  346. OverloadResolution GetOverloadResolution(const CFunction* other) {
  347. // Runtime overload resolution can only deal with functions with the
  348. // same number of arguments. Functions with different arity are handled
  349. // by compile time overload resolution though.
  350. if (ArgumentCount() != other->ArgumentCount()) {
  351. return OverloadResolution::kAtCompileTime;
  352. }
  353. // The functions can only differ by a single argument position.
  354. int diff_index = -1;
  355. for (unsigned int i = 0; i < ArgumentCount(); ++i) {
  356. if (ArgumentInfo(i).GetSequenceType() !=
  357. other->ArgumentInfo(i).GetSequenceType()) {
  358. if (diff_index >= 0) {
  359. return OverloadResolution::kImpossible;
  360. }
  361. diff_index = i;
  362. // We only support overload resolution between sequence types.
  363. if (ArgumentInfo(i).GetSequenceType() ==
  364. CTypeInfo::SequenceType::kScalar ||
  365. other->ArgumentInfo(i).GetSequenceType() ==
  366. CTypeInfo::SequenceType::kScalar) {
  367. return OverloadResolution::kImpossible;
  368. }
  369. }
  370. }
  371. return OverloadResolution::kAtRuntime;
  372. }
  373. template <typename F>
  374. static CFunction Make(F* func) {
  375. return ArgUnwrap<F*>::Make(func);
  376. }
  377. template <typename F>
  378. V8_DEPRECATED("Use CFunctionBuilder instead.")
  379. static CFunction MakeWithFallbackSupport(F* func) {
  380. return ArgUnwrap<F*>::Make(func);
  381. }
  382. CFunction(const void* address, const CFunctionInfo* type_info);
  383. private:
  384. const void* address_;
  385. const CFunctionInfo* type_info_;
  386. template <typename F>
  387. class ArgUnwrap {
  388. static_assert(sizeof(F) != sizeof(F),
  389. "CFunction must be created from a function pointer.");
  390. };
  391. template <typename R, typename... Args>
  392. class ArgUnwrap<R (*)(Args...)> {
  393. public:
  394. static CFunction Make(R (*func)(Args...));
  395. };
  396. };
  397. struct ApiObject {
  398. uintptr_t address;
  399. };
  400. /**
  401. * A struct which may be passed to a fast call callback, like so:
  402. * \code
  403. * void FastMethodWithOptions(int param, FastApiCallbackOptions& options);
  404. * \endcode
  405. */
  406. struct FastApiCallbackOptions {
  407. /**
  408. * Creates a new instance of FastApiCallbackOptions for testing purpose. The
  409. * returned instance may be filled with mock data.
  410. */
  411. static FastApiCallbackOptions CreateForTesting(Isolate* isolate) {
  412. return {false, {0}};
  413. }
  414. /**
  415. * If the callback wants to signal an error condition or to perform an
  416. * allocation, it must set options.fallback to true and do an early return
  417. * from the fast method. Then V8 checks the value of options.fallback and if
  418. * it's true, falls back to executing the SlowCallback, which is capable of
  419. * reporting the error (either by throwing a JS exception or logging to the
  420. * console) or doing the allocation. It's the embedder's responsibility to
  421. * ensure that the fast callback is idempotent up to the point where error and
  422. * fallback conditions are checked, because otherwise executing the slow
  423. * callback might produce visible side-effects twice.
  424. */
  425. bool fallback;
  426. /**
  427. * The `data` passed to the FunctionTemplate constructor, or `undefined`.
  428. * `data_ptr` allows for default constructing FastApiCallbackOptions.
  429. */
  430. union {
  431. uintptr_t data_ptr;
  432. v8::Value data;
  433. };
  434. };
  435. namespace internal {
  436. // Helper to count the number of occurances of `T` in `List`
  437. template <typename T, typename... List>
  438. struct count : std::integral_constant<int, 0> {};
  439. template <typename T, typename... Args>
  440. struct count<T, T, Args...>
  441. : std::integral_constant<std::size_t, 1 + count<T, Args...>::value> {};
  442. template <typename T, typename U, typename... Args>
  443. struct count<T, U, Args...> : count<T, Args...> {};
  444. template <typename RetBuilder, typename... ArgBuilders>
  445. class CFunctionInfoImpl : public CFunctionInfo {
  446. static constexpr int kOptionsArgCount =
  447. count<FastApiCallbackOptions&, ArgBuilders...>();
  448. static constexpr int kReceiverCount = 1;
  449. static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1,
  450. "Only one options parameter is supported.");
  451. static_assert(sizeof...(ArgBuilders) >= kOptionsArgCount + kReceiverCount,
  452. "The receiver or the options argument is missing.");
  453. public:
  454. constexpr CFunctionInfoImpl()
  455. : CFunctionInfo(RetBuilder::Build(), sizeof...(ArgBuilders),
  456. arg_info_storage_),
  457. arg_info_storage_{ArgBuilders::Build()...} {
  458. constexpr CTypeInfo::Type kReturnType = RetBuilder::Build().GetType();
  459. static_assert(kReturnType == CTypeInfo::Type::kVoid ||
  460. kReturnType == CTypeInfo::Type::kBool ||
  461. kReturnType == CTypeInfo::Type::kInt32 ||
  462. kReturnType == CTypeInfo::Type::kUint32 ||
  463. kReturnType == CTypeInfo::Type::kFloat32 ||
  464. kReturnType == CTypeInfo::Type::kFloat64,
  465. "64-bit int and api object values are not currently "
  466. "supported return types.");
  467. }
  468. private:
  469. const CTypeInfo arg_info_storage_[sizeof...(ArgBuilders)];
  470. };
  471. template <typename T>
  472. struct TypeInfoHelper {
  473. static_assert(sizeof(T) != sizeof(T), "This type is not supported");
  474. };
  475. #define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR(T, Enum) \
  476. template <> \
  477. struct TypeInfoHelper<T> { \
  478. static constexpr CTypeInfo::Flags Flags() { \
  479. return CTypeInfo::Flags::kNone; \
  480. } \
  481. \
  482. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \
  483. static constexpr CTypeInfo::SequenceType SequenceType() { \
  484. return CTypeInfo::SequenceType::kScalar; \
  485. } \
  486. };
  487. template <CTypeInfo::Type type>
  488. struct CTypeInfoTraits {};
  489. #define DEFINE_TYPE_INFO_TRAITS(CType, Enum) \
  490. template <> \
  491. struct CTypeInfoTraits<CTypeInfo::Type::Enum> { \
  492. using ctype = CType; \
  493. };
  494. #define PRIMITIVE_C_TYPES(V) \
  495. V(bool, kBool) \
  496. V(int32_t, kInt32) \
  497. V(uint32_t, kUint32) \
  498. V(int64_t, kInt64) \
  499. V(uint64_t, kUint64) \
  500. V(float, kFloat32) \
  501. V(double, kFloat64)
  502. // Same as above, but includes deprecated types for compatibility.
  503. #define ALL_C_TYPES(V) \
  504. PRIMITIVE_C_TYPES(V) \
  505. V(void, kVoid) \
  506. V(v8::Local<v8::Value>, kV8Value) \
  507. V(v8::Local<v8::Object>, kV8Value) \
  508. V(ApiObject, kApiObject)
  509. // ApiObject was a temporary solution to wrap the pointer to the v8::Value.
  510. // Please use v8::Local<v8::Value> in new code for the arguments and
  511. // v8::Local<v8::Object> for the receiver, as ApiObject will be deprecated.
  512. ALL_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR)
  513. PRIMITIVE_C_TYPES(DEFINE_TYPE_INFO_TRAITS)
  514. #undef PRIMITIVE_C_TYPES
  515. #undef ALL_C_TYPES
  516. #define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA(T, Enum) \
  517. template <> \
  518. struct TypeInfoHelper<FastApiTypedArray<T>> { \
  519. static constexpr CTypeInfo::Flags Flags() { \
  520. return CTypeInfo::Flags::kNone; \
  521. } \
  522. \
  523. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \
  524. static constexpr CTypeInfo::SequenceType SequenceType() { \
  525. return CTypeInfo::SequenceType::kIsTypedArray; \
  526. } \
  527. };
  528. #define TYPED_ARRAY_C_TYPES(V) \
  529. V(int32_t, kInt32) \
  530. V(uint32_t, kUint32) \
  531. V(int64_t, kInt64) \
  532. V(uint64_t, kUint64) \
  533. V(float, kFloat32) \
  534. V(double, kFloat64)
  535. TYPED_ARRAY_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA)
  536. #undef TYPED_ARRAY_C_TYPES
  537. template <>
  538. struct TypeInfoHelper<v8::Local<v8::Array>> {
  539. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  540. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kVoid; }
  541. static constexpr CTypeInfo::SequenceType SequenceType() {
  542. return CTypeInfo::SequenceType::kIsSequence;
  543. }
  544. };
  545. template <>
  546. struct TypeInfoHelper<v8::Local<v8::Uint32Array>> {
  547. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  548. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kUint32; }
  549. static constexpr CTypeInfo::SequenceType SequenceType() {
  550. return CTypeInfo::SequenceType::kIsTypedArray;
  551. }
  552. };
  553. template <>
  554. struct TypeInfoHelper<FastApiCallbackOptions&> {
  555. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  556. static constexpr CTypeInfo::Type Type() {
  557. return CTypeInfo::kCallbackOptionsType;
  558. }
  559. static constexpr CTypeInfo::SequenceType SequenceType() {
  560. return CTypeInfo::SequenceType::kScalar;
  561. }
  562. };
  563. #define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \
  564. static_assert(((COND) == 0) || (ASSERTION), MSG)
  565. template <typename T, CTypeInfo::Flags... Flags>
  566. class CTypeInfoBuilder {
  567. public:
  568. using BaseType = T;
  569. static constexpr CTypeInfo Build() {
  570. constexpr CTypeInfo::Flags kFlags =
  571. MergeFlags(TypeInfoHelper<T>::Flags(), Flags...);
  572. constexpr CTypeInfo::Type kType = TypeInfoHelper<T>::Type();
  573. constexpr CTypeInfo::SequenceType kSequenceType =
  574. TypeInfoHelper<T>::SequenceType();
  575. STATIC_ASSERT_IMPLIES(
  576. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kAllowSharedBit),
  577. (kSequenceType == CTypeInfo::SequenceType::kIsTypedArray ||
  578. kSequenceType == CTypeInfo::SequenceType::kIsArrayBuffer),
  579. "kAllowSharedBit is only allowed for TypedArrays and ArrayBuffers.");
  580. STATIC_ASSERT_IMPLIES(
  581. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kEnforceRangeBit),
  582. CTypeInfo::IsIntegralType(kType),
  583. "kEnforceRangeBit is only allowed for integral types.");
  584. STATIC_ASSERT_IMPLIES(
  585. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kClampBit),
  586. CTypeInfo::IsIntegralType(kType),
  587. "kClampBit is only allowed for integral types.");
  588. STATIC_ASSERT_IMPLIES(
  589. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kIsRestrictedBit),
  590. CTypeInfo::IsFloatingPointType(kType),
  591. "kIsRestrictedBit is only allowed for floating point types.");
  592. STATIC_ASSERT_IMPLIES(kSequenceType == CTypeInfo::SequenceType::kIsSequence,
  593. kType == CTypeInfo::Type::kVoid,
  594. "Sequences are only supported from void type.");
  595. STATIC_ASSERT_IMPLIES(
  596. kSequenceType == CTypeInfo::SequenceType::kIsTypedArray,
  597. CTypeInfo::IsPrimitive(kType) || kType == CTypeInfo::Type::kVoid,
  598. "TypedArrays are only supported from primitive types or void.");
  599. // Return the same type with the merged flags.
  600. return CTypeInfo(TypeInfoHelper<T>::Type(),
  601. TypeInfoHelper<T>::SequenceType(), kFlags);
  602. }
  603. private:
  604. template <typename... Rest>
  605. static constexpr CTypeInfo::Flags MergeFlags(CTypeInfo::Flags flags,
  606. Rest... rest) {
  607. return CTypeInfo::Flags(uint8_t(flags) | uint8_t(MergeFlags(rest...)));
  608. }
  609. static constexpr CTypeInfo::Flags MergeFlags() { return CTypeInfo::Flags(0); }
  610. };
  611. template <typename RetBuilder, typename... ArgBuilders>
  612. class CFunctionBuilderWithFunction {
  613. public:
  614. explicit constexpr CFunctionBuilderWithFunction(const void* fn) : fn_(fn) {}
  615. template <CTypeInfo::Flags... Flags>
  616. constexpr auto Ret() {
  617. return CFunctionBuilderWithFunction<
  618. CTypeInfoBuilder<typename RetBuilder::BaseType, Flags...>,
  619. ArgBuilders...>(fn_);
  620. }
  621. template <unsigned int N, CTypeInfo::Flags... Flags>
  622. constexpr auto Arg() {
  623. // Return a copy of the builder with the Nth arg builder merged with
  624. // template parameter pack Flags.
  625. return ArgImpl<N, Flags...>(
  626. std::make_index_sequence<sizeof...(ArgBuilders)>());
  627. }
  628. auto Build() {
  629. static CFunctionInfoImpl<RetBuilder, ArgBuilders...> instance;
  630. return CFunction(fn_, &instance);
  631. }
  632. private:
  633. template <bool Merge, unsigned int N, CTypeInfo::Flags... Flags>
  634. struct GetArgBuilder;
  635. // Returns the same ArgBuilder as the one at index N, including its flags.
  636. // Flags in the template parameter pack are ignored.
  637. template <unsigned int N, CTypeInfo::Flags... Flags>
  638. struct GetArgBuilder<false, N, Flags...> {
  639. using type =
  640. typename std::tuple_element<N, std::tuple<ArgBuilders...>>::type;
  641. };
  642. // Returns an ArgBuilder with the same base type as the one at index N,
  643. // but merges the flags with the flags in the template parameter pack.
  644. template <unsigned int N, CTypeInfo::Flags... Flags>
  645. struct GetArgBuilder<true, N, Flags...> {
  646. using type = CTypeInfoBuilder<
  647. typename std::tuple_element<N,
  648. std::tuple<ArgBuilders...>>::type::BaseType,
  649. std::tuple_element<N, std::tuple<ArgBuilders...>>::type::Build()
  650. .GetFlags(),
  651. Flags...>;
  652. };
  653. // Return a copy of the CFunctionBuilder, but merges the Flags on
  654. // ArgBuilder index N with the new Flags passed in the template parameter
  655. // pack.
  656. template <unsigned int N, CTypeInfo::Flags... Flags, size_t... I>
  657. constexpr auto ArgImpl(std::index_sequence<I...>) {
  658. return CFunctionBuilderWithFunction<
  659. RetBuilder, typename GetArgBuilder<N == I, I, Flags...>::type...>(fn_);
  660. }
  661. const void* fn_;
  662. };
  663. class CFunctionBuilder {
  664. public:
  665. constexpr CFunctionBuilder() {}
  666. template <typename R, typename... Args>
  667. constexpr auto Fn(R (*fn)(Args...)) {
  668. return CFunctionBuilderWithFunction<CTypeInfoBuilder<R>,
  669. CTypeInfoBuilder<Args>...>(
  670. reinterpret_cast<const void*>(fn));
  671. }
  672. };
  673. } // namespace internal
  674. // static
  675. template <typename R, typename... Args>
  676. CFunction CFunction::ArgUnwrap<R (*)(Args...)>::Make(R (*func)(Args...)) {
  677. return internal::CFunctionBuilder().Fn(func).Build();
  678. }
  679. using CFunctionBuilder = internal::CFunctionBuilder;
  680. /**
  681. * Copies the contents of this JavaScript array to a C++ buffer with
  682. * a given max_length. A CTypeInfo is passed as an argument,
  683. * instructing different rules for conversion (e.g. restricted float/double).
  684. * The element type T of the destination array must match the C type
  685. * corresponding to the CTypeInfo (specified by CTypeInfoTraits).
  686. * If the array length is larger than max_length or the array is of
  687. * unsupported type, the operation will fail, returning false. Generally, an
  688. * array which contains objects, undefined, null or anything not convertible
  689. * to the requested destination type, is considered unsupported. The operation
  690. * returns true on success. `type_info` will be used for conversions.
  691. */
  692. template <const CTypeInfo* type_info, typename T>
  693. bool CopyAndConvertArrayToCppBuffer(Local<Array> src, T* dst,
  694. uint32_t max_length);
  695. } // namespace v8
  696. #endif // INCLUDE_V8_FAST_API_CALLS_H_