v8-fast-api-calls.h 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  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. kAny, // This is added to enable untyped representation of fast
  245. // call arguments for test purposes. It can represent any of
  246. // the other types stored in the same memory as a union (see
  247. // the AnyCType struct declared below). This allows for
  248. // uniform passing of arguments w.r.t. their location
  249. // (in a register or on the stack), independent of their
  250. // actual type. It's currently used by the arm64 simulator
  251. // and can be added to the other simulators as well when fast
  252. // calls having both GP and FP params need to be supported.
  253. };
  254. // kCallbackOptionsType is not part of the Type enum
  255. // because it is only used internally. Use value 255 that is larger
  256. // than any valid Type enum.
  257. static constexpr Type kCallbackOptionsType = Type(255);
  258. enum class SequenceType : uint8_t {
  259. kScalar,
  260. kIsSequence, // sequence<T>
  261. kIsTypedArray, // TypedArray of T or any ArrayBufferView if T
  262. // is void
  263. kIsArrayBuffer // ArrayBuffer
  264. };
  265. enum class Flags : uint8_t {
  266. kNone = 0,
  267. kAllowSharedBit = 1 << 0, // Must be an ArrayBuffer or TypedArray
  268. kEnforceRangeBit = 1 << 1, // T must be integral
  269. kClampBit = 1 << 2, // T must be integral
  270. kIsRestrictedBit = 1 << 3, // T must be float or double
  271. };
  272. explicit constexpr CTypeInfo(
  273. Type type, SequenceType sequence_type = SequenceType::kScalar,
  274. Flags flags = Flags::kNone)
  275. : type_(type), sequence_type_(sequence_type), flags_(flags) {}
  276. typedef uint32_t Identifier;
  277. explicit constexpr CTypeInfo(Identifier identifier)
  278. : CTypeInfo(static_cast<Type>(identifier >> 16),
  279. static_cast<SequenceType>((identifier >> 8) & 255),
  280. static_cast<Flags>(identifier & 255)) {}
  281. constexpr Identifier GetId() const {
  282. return static_cast<uint8_t>(type_) << 16 |
  283. static_cast<uint8_t>(sequence_type_) << 8 |
  284. static_cast<uint8_t>(flags_);
  285. }
  286. constexpr Type GetType() const { return type_; }
  287. constexpr SequenceType GetSequenceType() const { return sequence_type_; }
  288. constexpr Flags GetFlags() const { return flags_; }
  289. static constexpr bool IsIntegralType(Type type) {
  290. return type == Type::kInt32 || type == Type::kUint32 ||
  291. type == Type::kInt64 || type == Type::kUint64;
  292. }
  293. static constexpr bool IsFloatingPointType(Type type) {
  294. return type == Type::kFloat32 || type == Type::kFloat64;
  295. }
  296. static constexpr bool IsPrimitive(Type type) {
  297. return IsIntegralType(type) || IsFloatingPointType(type) ||
  298. type == Type::kBool;
  299. }
  300. private:
  301. Type type_;
  302. SequenceType sequence_type_;
  303. Flags flags_;
  304. };
  305. struct FastApiTypedArrayBase {
  306. public:
  307. // Returns the length in number of elements.
  308. size_t V8_EXPORT length() const { return length_; }
  309. // Checks whether the given index is within the bounds of the collection.
  310. void V8_EXPORT ValidateIndex(size_t index) const;
  311. protected:
  312. size_t length_ = 0;
  313. };
  314. template <typename T>
  315. struct FastApiTypedArray : public FastApiTypedArrayBase {
  316. public:
  317. V8_INLINE T get(size_t index) const {
  318. #ifdef DEBUG
  319. ValidateIndex(index);
  320. #endif // DEBUG
  321. T tmp;
  322. memcpy(&tmp, reinterpret_cast<T*>(data_) + index, sizeof(T));
  323. return tmp;
  324. }
  325. bool getStorageIfAligned(T** elements) const {
  326. if (reinterpret_cast<uintptr_t>(data_) % alignof(T) != 0) {
  327. return false;
  328. }
  329. *elements = reinterpret_cast<T*>(data_);
  330. return true;
  331. }
  332. private:
  333. // This pointer should include the typed array offset applied.
  334. // It's not guaranteed that it's aligned to sizeof(T), it's only
  335. // guaranteed that it's 4-byte aligned, so for 8-byte types we need to
  336. // provide a special implementation for reading from it, which hides
  337. // the possibly unaligned read in the `get` method.
  338. void* data_;
  339. };
  340. // Any TypedArray. It uses kTypedArrayBit with base type void
  341. // Overloaded args of ArrayBufferView and TypedArray are not supported
  342. // (for now) because the generic “any” ArrayBufferView doesn’t have its
  343. // own instance type. It could be supported if we specify that
  344. // TypedArray<T> always has precedence over the generic ArrayBufferView,
  345. // but this complicates overload resolution.
  346. struct FastApiArrayBufferView {
  347. void* data;
  348. size_t byte_length;
  349. };
  350. struct FastApiArrayBuffer {
  351. void* data;
  352. size_t byte_length;
  353. };
  354. class V8_EXPORT CFunctionInfo {
  355. public:
  356. // Construct a struct to hold a CFunction's type information.
  357. // |return_info| describes the function's return type.
  358. // |arg_info| is an array of |arg_count| CTypeInfos describing the
  359. // arguments. Only the last argument may be of the special type
  360. // CTypeInfo::kCallbackOptionsType.
  361. CFunctionInfo(const CTypeInfo& return_info, unsigned int arg_count,
  362. const CTypeInfo* arg_info);
  363. const CTypeInfo& ReturnInfo() const { return return_info_; }
  364. // The argument count, not including the v8::FastApiCallbackOptions
  365. // if present.
  366. unsigned int ArgumentCount() const {
  367. return HasOptions() ? arg_count_ - 1 : arg_count_;
  368. }
  369. // |index| must be less than ArgumentCount().
  370. // Note: if the last argument passed on construction of CFunctionInfo
  371. // has type CTypeInfo::kCallbackOptionsType, it is not included in
  372. // ArgumentCount().
  373. const CTypeInfo& ArgumentInfo(unsigned int index) const;
  374. bool HasOptions() const {
  375. // The options arg is always the last one.
  376. return arg_count_ > 0 && arg_info_[arg_count_ - 1].GetType() ==
  377. CTypeInfo::kCallbackOptionsType;
  378. }
  379. private:
  380. const CTypeInfo return_info_;
  381. const unsigned int arg_count_;
  382. const CTypeInfo* arg_info_;
  383. };
  384. struct FastApiCallbackOptions;
  385. // Provided for testing.
  386. struct AnyCType {
  387. AnyCType() : int64_value(0) {}
  388. union {
  389. bool bool_value;
  390. int32_t int32_value;
  391. uint32_t uint32_value;
  392. int64_t int64_value;
  393. uint64_t uint64_value;
  394. float float_value;
  395. double double_value;
  396. Local<Object> object_value;
  397. Local<Array> sequence_value;
  398. const FastApiTypedArray<int32_t>* int32_ta_value;
  399. const FastApiTypedArray<uint32_t>* uint32_ta_value;
  400. const FastApiTypedArray<int64_t>* int64_ta_value;
  401. const FastApiTypedArray<uint64_t>* uint64_ta_value;
  402. const FastApiTypedArray<float>* float_ta_value;
  403. const FastApiTypedArray<double>* double_ta_value;
  404. FastApiCallbackOptions* options_value;
  405. };
  406. };
  407. static_assert(
  408. sizeof(AnyCType) == 8,
  409. "The AnyCType struct should have size == 64 bits, as this is assumed "
  410. "by EffectControlLinearizer.");
  411. class V8_EXPORT CFunction {
  412. public:
  413. constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
  414. const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
  415. const CTypeInfo& ArgumentInfo(unsigned int index) const {
  416. return type_info_->ArgumentInfo(index);
  417. }
  418. unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
  419. const void* GetAddress() const { return address_; }
  420. const CFunctionInfo* GetTypeInfo() const { return type_info_; }
  421. enum class OverloadResolution { kImpossible, kAtRuntime, kAtCompileTime };
  422. // Returns whether an overload between this and the given CFunction can
  423. // be resolved at runtime by the RTTI available for the arguments or at
  424. // compile time for functions with different number of arguments.
  425. OverloadResolution GetOverloadResolution(const CFunction* other) {
  426. // Runtime overload resolution can only deal with functions with the
  427. // same number of arguments. Functions with different arity are handled
  428. // by compile time overload resolution though.
  429. if (ArgumentCount() != other->ArgumentCount()) {
  430. return OverloadResolution::kAtCompileTime;
  431. }
  432. // The functions can only differ by a single argument position.
  433. int diff_index = -1;
  434. for (unsigned int i = 0; i < ArgumentCount(); ++i) {
  435. if (ArgumentInfo(i).GetSequenceType() !=
  436. other->ArgumentInfo(i).GetSequenceType()) {
  437. if (diff_index >= 0) {
  438. return OverloadResolution::kImpossible;
  439. }
  440. diff_index = i;
  441. // We only support overload resolution between sequence types.
  442. if (ArgumentInfo(i).GetSequenceType() ==
  443. CTypeInfo::SequenceType::kScalar ||
  444. other->ArgumentInfo(i).GetSequenceType() ==
  445. CTypeInfo::SequenceType::kScalar) {
  446. return OverloadResolution::kImpossible;
  447. }
  448. }
  449. }
  450. return OverloadResolution::kAtRuntime;
  451. }
  452. template <typename F>
  453. static CFunction Make(F* func) {
  454. return ArgUnwrap<F*>::Make(func);
  455. }
  456. // Provided for testing purposes.
  457. template <typename R, typename... Args, typename R_Patch,
  458. typename... Args_Patch>
  459. static CFunction Make(R (*func)(Args...),
  460. R_Patch (*patching_func)(Args_Patch...)) {
  461. CFunction c_func = ArgUnwrap<R (*)(Args...)>::Make(func);
  462. static_assert(
  463. sizeof...(Args_Patch) == sizeof...(Args),
  464. "The patching function must have the same number of arguments.");
  465. c_func.address_ = reinterpret_cast<void*>(patching_func);
  466. return c_func;
  467. }
  468. CFunction(const void* address, const CFunctionInfo* type_info);
  469. private:
  470. const void* address_;
  471. const CFunctionInfo* type_info_;
  472. template <typename F>
  473. class ArgUnwrap {
  474. static_assert(sizeof(F) != sizeof(F),
  475. "CFunction must be created from a function pointer.");
  476. };
  477. template <typename R, typename... Args>
  478. class ArgUnwrap<R (*)(Args...)> {
  479. public:
  480. static CFunction Make(R (*func)(Args...));
  481. };
  482. };
  483. /**
  484. * A struct which may be passed to a fast call callback, like so:
  485. * \code
  486. * void FastMethodWithOptions(int param, FastApiCallbackOptions& options);
  487. * \endcode
  488. */
  489. struct FastApiCallbackOptions {
  490. /**
  491. * Creates a new instance of FastApiCallbackOptions for testing purpose. The
  492. * returned instance may be filled with mock data.
  493. */
  494. static FastApiCallbackOptions CreateForTesting(Isolate* isolate) {
  495. return {false, {0}};
  496. }
  497. /**
  498. * If the callback wants to signal an error condition or to perform an
  499. * allocation, it must set options.fallback to true and do an early return
  500. * from the fast method. Then V8 checks the value of options.fallback and if
  501. * it's true, falls back to executing the SlowCallback, which is capable of
  502. * reporting the error (either by throwing a JS exception or logging to the
  503. * console) or doing the allocation. It's the embedder's responsibility to
  504. * ensure that the fast callback is idempotent up to the point where error and
  505. * fallback conditions are checked, because otherwise executing the slow
  506. * callback might produce visible side-effects twice.
  507. */
  508. bool fallback;
  509. /**
  510. * The `data` passed to the FunctionTemplate constructor, or `undefined`.
  511. * `data_ptr` allows for default constructing FastApiCallbackOptions.
  512. */
  513. union {
  514. uintptr_t data_ptr;
  515. v8::Value data;
  516. };
  517. };
  518. namespace internal {
  519. // Helper to count the number of occurances of `T` in `List`
  520. template <typename T, typename... List>
  521. struct count : std::integral_constant<int, 0> {};
  522. template <typename T, typename... Args>
  523. struct count<T, T, Args...>
  524. : std::integral_constant<std::size_t, 1 + count<T, Args...>::value> {};
  525. template <typename T, typename U, typename... Args>
  526. struct count<T, U, Args...> : count<T, Args...> {};
  527. template <typename RetBuilder, typename... ArgBuilders>
  528. class CFunctionInfoImpl : public CFunctionInfo {
  529. static constexpr int kOptionsArgCount =
  530. count<FastApiCallbackOptions&, ArgBuilders...>();
  531. static constexpr int kReceiverCount = 1;
  532. static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1,
  533. "Only one options parameter is supported.");
  534. static_assert(sizeof...(ArgBuilders) >= kOptionsArgCount + kReceiverCount,
  535. "The receiver or the options argument is missing.");
  536. public:
  537. constexpr CFunctionInfoImpl()
  538. : CFunctionInfo(RetBuilder::Build(), sizeof...(ArgBuilders),
  539. arg_info_storage_),
  540. arg_info_storage_{ArgBuilders::Build()...} {
  541. constexpr CTypeInfo::Type kReturnType = RetBuilder::Build().GetType();
  542. static_assert(kReturnType == CTypeInfo::Type::kVoid ||
  543. kReturnType == CTypeInfo::Type::kBool ||
  544. kReturnType == CTypeInfo::Type::kInt32 ||
  545. kReturnType == CTypeInfo::Type::kUint32 ||
  546. kReturnType == CTypeInfo::Type::kFloat32 ||
  547. kReturnType == CTypeInfo::Type::kFloat64 ||
  548. kReturnType == CTypeInfo::Type::kAny,
  549. "64-bit int and api object values are not currently "
  550. "supported return types.");
  551. }
  552. private:
  553. const CTypeInfo arg_info_storage_[sizeof...(ArgBuilders)];
  554. };
  555. template <typename T>
  556. struct TypeInfoHelper {
  557. static_assert(sizeof(T) != sizeof(T), "This type is not supported");
  558. };
  559. #define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR(T, Enum) \
  560. template <> \
  561. struct TypeInfoHelper<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::kScalar; \
  569. } \
  570. };
  571. template <CTypeInfo::Type type>
  572. struct CTypeInfoTraits {};
  573. #define DEFINE_TYPE_INFO_TRAITS(CType, Enum) \
  574. template <> \
  575. struct CTypeInfoTraits<CTypeInfo::Type::Enum> { \
  576. using ctype = CType; \
  577. };
  578. #define PRIMITIVE_C_TYPES(V) \
  579. V(bool, kBool) \
  580. V(int32_t, kInt32) \
  581. V(uint32_t, kUint32) \
  582. V(int64_t, kInt64) \
  583. V(uint64_t, kUint64) \
  584. V(float, kFloat32) \
  585. V(double, kFloat64)
  586. // Same as above, but includes deprecated types for compatibility.
  587. #define ALL_C_TYPES(V) \
  588. PRIMITIVE_C_TYPES(V) \
  589. V(void, kVoid) \
  590. V(v8::Local<v8::Value>, kV8Value) \
  591. V(v8::Local<v8::Object>, kV8Value) \
  592. V(AnyCType, kAny)
  593. // ApiObject was a temporary solution to wrap the pointer to the v8::Value.
  594. // Please use v8::Local<v8::Value> in new code for the arguments and
  595. // v8::Local<v8::Object> for the receiver, as ApiObject will be deprecated.
  596. ALL_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR)
  597. PRIMITIVE_C_TYPES(DEFINE_TYPE_INFO_TRAITS)
  598. #undef PRIMITIVE_C_TYPES
  599. #undef ALL_C_TYPES
  600. #define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA(T, Enum) \
  601. template <> \
  602. struct TypeInfoHelper<const FastApiTypedArray<T>&> { \
  603. static constexpr CTypeInfo::Flags Flags() { \
  604. return CTypeInfo::Flags::kNone; \
  605. } \
  606. \
  607. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \
  608. static constexpr CTypeInfo::SequenceType SequenceType() { \
  609. return CTypeInfo::SequenceType::kIsTypedArray; \
  610. } \
  611. };
  612. #define TYPED_ARRAY_C_TYPES(V) \
  613. V(int32_t, kInt32) \
  614. V(uint32_t, kUint32) \
  615. V(int64_t, kInt64) \
  616. V(uint64_t, kUint64) \
  617. V(float, kFloat32) \
  618. V(double, kFloat64)
  619. TYPED_ARRAY_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA)
  620. #undef TYPED_ARRAY_C_TYPES
  621. template <>
  622. struct TypeInfoHelper<v8::Local<v8::Array>> {
  623. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  624. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kVoid; }
  625. static constexpr CTypeInfo::SequenceType SequenceType() {
  626. return CTypeInfo::SequenceType::kIsSequence;
  627. }
  628. };
  629. template <>
  630. struct TypeInfoHelper<v8::Local<v8::Uint32Array>> {
  631. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  632. static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kUint32; }
  633. static constexpr CTypeInfo::SequenceType SequenceType() {
  634. return CTypeInfo::SequenceType::kIsTypedArray;
  635. }
  636. };
  637. template <>
  638. struct TypeInfoHelper<FastApiCallbackOptions&> {
  639. static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
  640. static constexpr CTypeInfo::Type Type() {
  641. return CTypeInfo::kCallbackOptionsType;
  642. }
  643. static constexpr CTypeInfo::SequenceType SequenceType() {
  644. return CTypeInfo::SequenceType::kScalar;
  645. }
  646. };
  647. #define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \
  648. static_assert(((COND) == 0) || (ASSERTION), MSG)
  649. } // namespace internal
  650. template <typename T, CTypeInfo::Flags... Flags>
  651. class V8_EXPORT CTypeInfoBuilder {
  652. public:
  653. using BaseType = T;
  654. static constexpr CTypeInfo Build() {
  655. constexpr CTypeInfo::Flags kFlags =
  656. MergeFlags(internal::TypeInfoHelper<T>::Flags(), Flags...);
  657. constexpr CTypeInfo::Type kType = internal::TypeInfoHelper<T>::Type();
  658. constexpr CTypeInfo::SequenceType kSequenceType =
  659. internal::TypeInfoHelper<T>::SequenceType();
  660. STATIC_ASSERT_IMPLIES(
  661. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kAllowSharedBit),
  662. (kSequenceType == CTypeInfo::SequenceType::kIsTypedArray ||
  663. kSequenceType == CTypeInfo::SequenceType::kIsArrayBuffer),
  664. "kAllowSharedBit is only allowed for TypedArrays and ArrayBuffers.");
  665. STATIC_ASSERT_IMPLIES(
  666. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kEnforceRangeBit),
  667. CTypeInfo::IsIntegralType(kType),
  668. "kEnforceRangeBit is only allowed for integral types.");
  669. STATIC_ASSERT_IMPLIES(
  670. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kClampBit),
  671. CTypeInfo::IsIntegralType(kType),
  672. "kClampBit is only allowed for integral types.");
  673. STATIC_ASSERT_IMPLIES(
  674. uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kIsRestrictedBit),
  675. CTypeInfo::IsFloatingPointType(kType),
  676. "kIsRestrictedBit is only allowed for floating point types.");
  677. STATIC_ASSERT_IMPLIES(kSequenceType == CTypeInfo::SequenceType::kIsSequence,
  678. kType == CTypeInfo::Type::kVoid,
  679. "Sequences are only supported from void type.");
  680. STATIC_ASSERT_IMPLIES(
  681. kSequenceType == CTypeInfo::SequenceType::kIsTypedArray,
  682. CTypeInfo::IsPrimitive(kType) || kType == CTypeInfo::Type::kVoid,
  683. "TypedArrays are only supported from primitive types or void.");
  684. // Return the same type with the merged flags.
  685. return CTypeInfo(internal::TypeInfoHelper<T>::Type(),
  686. internal::TypeInfoHelper<T>::SequenceType(), kFlags);
  687. }
  688. private:
  689. template <typename... Rest>
  690. static constexpr CTypeInfo::Flags MergeFlags(CTypeInfo::Flags flags,
  691. Rest... rest) {
  692. return CTypeInfo::Flags(uint8_t(flags) | uint8_t(MergeFlags(rest...)));
  693. }
  694. static constexpr CTypeInfo::Flags MergeFlags() { return CTypeInfo::Flags(0); }
  695. };
  696. namespace internal {
  697. template <typename RetBuilder, typename... ArgBuilders>
  698. class CFunctionBuilderWithFunction {
  699. public:
  700. explicit constexpr CFunctionBuilderWithFunction(const void* fn) : fn_(fn) {}
  701. template <CTypeInfo::Flags... Flags>
  702. constexpr auto Ret() {
  703. return CFunctionBuilderWithFunction<
  704. CTypeInfoBuilder<typename RetBuilder::BaseType, Flags...>,
  705. ArgBuilders...>(fn_);
  706. }
  707. template <unsigned int N, CTypeInfo::Flags... Flags>
  708. constexpr auto Arg() {
  709. // Return a copy of the builder with the Nth arg builder merged with
  710. // template parameter pack Flags.
  711. return ArgImpl<N, Flags...>(
  712. std::make_index_sequence<sizeof...(ArgBuilders)>());
  713. }
  714. auto Build() {
  715. static CFunctionInfoImpl<RetBuilder, ArgBuilders...> instance;
  716. return CFunction(fn_, &instance);
  717. }
  718. private:
  719. template <bool Merge, unsigned int N, CTypeInfo::Flags... Flags>
  720. struct GetArgBuilder;
  721. // Returns the same ArgBuilder as the one at index N, including its flags.
  722. // Flags in the template parameter pack are ignored.
  723. template <unsigned int N, CTypeInfo::Flags... Flags>
  724. struct GetArgBuilder<false, N, Flags...> {
  725. using type =
  726. typename std::tuple_element<N, std::tuple<ArgBuilders...>>::type;
  727. };
  728. // Returns an ArgBuilder with the same base type as the one at index N,
  729. // but merges the flags with the flags in the template parameter pack.
  730. template <unsigned int N, CTypeInfo::Flags... Flags>
  731. struct GetArgBuilder<true, N, Flags...> {
  732. using type = CTypeInfoBuilder<
  733. typename std::tuple_element<N,
  734. std::tuple<ArgBuilders...>>::type::BaseType,
  735. std::tuple_element<N, std::tuple<ArgBuilders...>>::type::Build()
  736. .GetFlags(),
  737. Flags...>;
  738. };
  739. // Return a copy of the CFunctionBuilder, but merges the Flags on
  740. // ArgBuilder index N with the new Flags passed in the template parameter
  741. // pack.
  742. template <unsigned int N, CTypeInfo::Flags... Flags, size_t... I>
  743. constexpr auto ArgImpl(std::index_sequence<I...>) {
  744. return CFunctionBuilderWithFunction<
  745. RetBuilder, typename GetArgBuilder<N == I, I, Flags...>::type...>(fn_);
  746. }
  747. const void* fn_;
  748. };
  749. class CFunctionBuilder {
  750. public:
  751. constexpr CFunctionBuilder() {}
  752. template <typename R, typename... Args>
  753. constexpr auto Fn(R (*fn)(Args...)) {
  754. return CFunctionBuilderWithFunction<CTypeInfoBuilder<R>,
  755. CTypeInfoBuilder<Args>...>(
  756. reinterpret_cast<const void*>(fn));
  757. }
  758. };
  759. } // namespace internal
  760. // static
  761. template <typename R, typename... Args>
  762. CFunction CFunction::ArgUnwrap<R (*)(Args...)>::Make(R (*func)(Args...)) {
  763. return internal::CFunctionBuilder().Fn(func).Build();
  764. }
  765. using CFunctionBuilder = internal::CFunctionBuilder;
  766. static constexpr CTypeInfo kTypeInfoInt32 = CTypeInfo(CTypeInfo::Type::kInt32);
  767. static constexpr CTypeInfo kTypeInfoFloat64 =
  768. CTypeInfo(CTypeInfo::Type::kFloat64);
  769. /**
  770. * Copies the contents of this JavaScript array to a C++ buffer with
  771. * a given max_length. A CTypeInfo is passed as an argument,
  772. * instructing different rules for conversion (e.g. restricted float/double).
  773. * The element type T of the destination array must match the C type
  774. * corresponding to the CTypeInfo (specified by CTypeInfoTraits).
  775. * If the array length is larger than max_length or the array is of
  776. * unsupported type, the operation will fail, returning false. Generally, an
  777. * array which contains objects, undefined, null or anything not convertible
  778. * to the requested destination type, is considered unsupported. The operation
  779. * returns true on success. `type_info` will be used for conversions.
  780. */
  781. template <const CTypeInfo* type_info, typename T>
  782. V8_DEPRECATED(
  783. "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
  784. bool V8_EXPORT V8_WARN_UNUSED_RESULT
  785. TryCopyAndConvertArrayToCppBuffer(Local<Array> src, T* dst,
  786. uint32_t max_length);
  787. template <>
  788. V8_DEPRECATED(
  789. "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
  790. inline bool V8_WARN_UNUSED_RESULT
  791. TryCopyAndConvertArrayToCppBuffer<&kTypeInfoInt32, int32_t>(
  792. Local<Array> src, int32_t* dst, uint32_t max_length) {
  793. return false;
  794. }
  795. template <>
  796. V8_DEPRECATED(
  797. "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
  798. inline bool V8_WARN_UNUSED_RESULT
  799. TryCopyAndConvertArrayToCppBuffer<&kTypeInfoFloat64, double>(
  800. Local<Array> src, double* dst, uint32_t max_length) {
  801. return false;
  802. }
  803. template <CTypeInfo::Identifier type_info_id, typename T>
  804. bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer(
  805. Local<Array> src, T* dst, uint32_t max_length);
  806. template <>
  807. bool V8_EXPORT V8_WARN_UNUSED_RESULT
  808. TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<int32_t>::Build().GetId(),
  809. int32_t>(Local<Array> src, int32_t* dst,
  810. uint32_t max_length);
  811. template <>
  812. bool V8_EXPORT V8_WARN_UNUSED_RESULT
  813. TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<uint32_t>::Build().GetId(),
  814. uint32_t>(Local<Array> src, uint32_t* dst,
  815. uint32_t max_length);
  816. template <>
  817. bool V8_EXPORT V8_WARN_UNUSED_RESULT
  818. TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<float>::Build().GetId(),
  819. float>(Local<Array> src, float* dst,
  820. uint32_t max_length);
  821. template <>
  822. bool V8_EXPORT V8_WARN_UNUSED_RESULT
  823. TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<double>::Build().GetId(),
  824. double>(Local<Array> src, double* dst,
  825. uint32_t max_length);
  826. } // namespace v8
  827. #endif // INCLUDE_V8_FAST_API_CALLS_H_