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